auth
This commit is contained in:
15
src/app/admin/page.tsx
Normal file
15
src/app/admin/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { SignedIn } from "@clerk/nextjs";
|
||||
|
||||
const AdminPage = async () => {
|
||||
return (
|
||||
<SignedIn>
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-black text-white">
|
||||
<div>
|
||||
hello admin
|
||||
</div>
|
||||
</main>
|
||||
</SignedIn>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminPage;
|
||||
@@ -1,12 +1,15 @@
|
||||
import "~/styles/globals.css";
|
||||
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { Geist } from "next/font/google";
|
||||
|
||||
import { ClerkProvider, SignedIn, SignedOut, SignUpButton, UserButton } from "@clerk/nextjs";
|
||||
import { auth } from "@clerk/nextjs/server";
|
||||
import { env } from "~/env";
|
||||
export const metadata: Metadata = {
|
||||
title: "Create T3 App",
|
||||
description: "Generated by create-t3-app",
|
||||
icons: [{ rel: "icon", url: "/favicon.ico" }],
|
||||
title: "Gregor Lohaus",
|
||||
description: "My Personal Website",
|
||||
icons: [{ rel: "icon", url: "/GLIcon.svg" }],
|
||||
};
|
||||
|
||||
const geist = Geist({
|
||||
@@ -14,13 +17,32 @@ const geist = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
});
|
||||
|
||||
const TopNav = () => {
|
||||
const AdminWrap = async ({children,}: Readonly<{ children: React.ReactNode }>) => {
|
||||
const userid = (await auth()).userId
|
||||
const isAdmin = (userid == env.ADMIN_USER_CLERK_ID)
|
||||
if (isAdmin) {
|
||||
return <>{children}</>
|
||||
}
|
||||
return (<></>)
|
||||
}
|
||||
|
||||
const TopNav = async () => {
|
||||
return (
|
||||
<nav className="flex w-full border-b p-4 gap-5 bg-black text-white border-white">
|
||||
<div> Blog </div>
|
||||
<div> CV </div>
|
||||
<div> Projects </div>
|
||||
<div> Fun </div>
|
||||
<nav className="flex flex-wrap items-center w-full border-b px-5 py-5 gap-5 bg-black text-white border-white">
|
||||
<Link className="h-fit" href={"/blog"}> Blog </Link>
|
||||
<Link className="h-fit" href={"/cv"}> CV </Link>
|
||||
<Link className="h-fit" href={"/projects"}> Projects </Link>
|
||||
<Link className="h-fit" href={"/fun"}> Fun </Link>
|
||||
<div className="ml-auto"/>
|
||||
<AdminWrap><Link className="h-fit" href={"/admin"}> Admin </Link></AdminWrap>
|
||||
<div className="h-fit flex">
|
||||
<SignedIn>
|
||||
<UserButton/>
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<SignUpButton/>
|
||||
</SignedOut>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -29,11 +51,13 @@ export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<html lang="en" className={`${geist.variable}`}>
|
||||
<body className="flex flex-col gap-2 bg-black text-white">
|
||||
<TopNav/>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
<ClerkProvider>
|
||||
<html lang="en" className={`${geist.variable}`}>
|
||||
<body className="flex flex-col gap-2 bg-black text-white">
|
||||
<TopNav/>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
</ClerkProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import Link from "next/link";
|
||||
import { db } from "~/server/db";
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
export default async function HomePage() {
|
||||
const posts = await db.query.posts.findMany()
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-black text-white">
|
||||
<div>
|
||||
{posts.map((post) => {
|
||||
return (
|
||||
<div key={post.id}>
|
||||
{post.name}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
hello world
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ export const env = createEnv({
|
||||
*/
|
||||
server: {
|
||||
DATABASE_URL: z.string().url(),
|
||||
ADMIN_USER_CLERK_ID: z.string(),
|
||||
NODE_ENV: z
|
||||
.enum(["development", "test", "production"])
|
||||
.default("development"),
|
||||
@@ -28,6 +29,7 @@ export const env = createEnv({
|
||||
*/
|
||||
runtimeEnv: {
|
||||
DATABASE_URL: process.env.DATABASE_URL,
|
||||
ADMIN_USER_CLERK_ID: process.env.ADMIN_USER_CLERK_ID,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
|
||||
},
|
||||
|
||||
21
src/middleware.ts
Normal file
21
src/middleware.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { clerkMiddleware, createRouteMatcher, currentUser } from "@clerk/nextjs/server";
|
||||
import { env } from "~/env";
|
||||
|
||||
const isTenantAdminRoute = createRouteMatcher(['/admin(.*)'])
|
||||
export default clerkMiddleware(async (auth,req) => {
|
||||
if (isTenantAdminRoute(req)) {
|
||||
let userid = (await auth()).userId
|
||||
if (userid != env.ADMIN_USER_CLERK_ID) {
|
||||
await auth.protect()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
// Skip Next.js internals and all static files, unless found in search params
|
||||
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
|
||||
// Always run for API routes
|
||||
'/(api|trpc)(.*)',
|
||||
],
|
||||
};
|
||||
1
src/server/db/query.ts
Normal file
1
src/server/db/query.ts
Normal file
@@ -0,0 +1 @@
|
||||
import "server-only"
|
||||
@@ -1,7 +1,7 @@
|
||||
// Example model schema from the Drizzle docs
|
||||
// https://orm.drizzle.team/docs/sql-schema-declaration
|
||||
|
||||
import { sql } from "drizzle-orm";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { index, pgTableCreator } from "drizzle-orm/pg-core";
|
||||
|
||||
/**
|
||||
@@ -12,16 +12,37 @@ import { index, pgTableCreator } from "drizzle-orm/pg-core";
|
||||
*/
|
||||
export const createTable = pgTableCreator((name) => `gregorlohaus.com_${name}`);
|
||||
|
||||
export const posts = createTable(
|
||||
"post",
|
||||
export const cvCategory = createTable(
|
||||
"cv_category",
|
||||
(d) => ({
|
||||
id: d.integer().primaryKey().generatedByDefaultAsIdentity(),
|
||||
name: d.varchar({ length: 256 }),
|
||||
id: d.uuid().primaryKey(),
|
||||
name: d.varchar({length: 50})
|
||||
}),
|
||||
(t) => [index("name_idx").on(t.name)],
|
||||
)
|
||||
|
||||
export const cvCategoryRelations = relations(cvCategory,({many}) => ({
|
||||
cvEntry: many(cvEntry)
|
||||
}))
|
||||
|
||||
export const cvEntry = createTable(
|
||||
"cv_entry",
|
||||
(d) => ({
|
||||
id: d.uuid().primaryKey().notNull(),
|
||||
categoryId: d.uuid('category_id'),
|
||||
fromTime: d.date().notNull(),
|
||||
toTime: d.date().notNull(),
|
||||
createdAt: d
|
||||
.timestamp({ withTimezone: true })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()),
|
||||
})
|
||||
)
|
||||
|
||||
export const cvEntryRelations = relations(cvEntry, ({one}) => ({
|
||||
category: one(cvCategory, {
|
||||
fields: [cvEntry.categoryId],
|
||||
references: [cvCategory.id]
|
||||
}),
|
||||
(t) => [index("name_idx").on(t.name)],
|
||||
);
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user