This commit is contained in:
2025-04-08 04:05:57 +02:00
parent cdd9f01214
commit 87832f46a3
14 changed files with 155 additions and 42 deletions

View File

@@ -0,0 +1,4 @@
export default function Default() {
return null
}

View File

@@ -0,0 +1,9 @@
import { isAdmin } from "../actions"
export default async function AdminWrap({children,}: Readonly<{ children: React.ReactNode }>) {
if (await isAdmin()) {
return <>{children}</>
}
return (<></>)
}

View File

@@ -0,0 +1,25 @@
import Link from "next/link"
import AdminWrap from "./AdminWrap"
import { SignedIn, SignedOut, SignUpButton, UserButton } from "@clerk/nextjs"
export default function TopNav() {
return (
<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>
)
}

8
src/app/actions.ts Normal file
View File

@@ -0,0 +1,8 @@
'use server'
import { auth } from "@clerk/nextjs/server"
import { env } from "~/env"
export async function isAdmin() {
const userid = (await auth()).userId
return (userid == env.ADMIN_USER_CLERK_ID)
}

View File

@@ -1,6 +1,6 @@
import { SignedIn } from "@clerk/nextjs";
const AdminPage = async () => {
export default function AdminPage() {
return (
<SignedIn>
<main className="flex min-h-screen flex-col items-center justify-center bg-black text-white">
@@ -11,5 +11,3 @@ const AdminPage = async () => {
</SignedIn>
)
}
export default AdminPage;

10
src/app/blog/layout.tsx Normal file
View File

@@ -0,0 +1,10 @@
'use client'
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode}>) {
return (
<>
{children}
</>
)
}

10
src/app/cv/layout.tsx Normal file
View File

@@ -0,0 +1,10 @@
'use client'
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode}>) {
return (
<>
{children}
</>
)
}

18
src/app/cv/page.tsx Normal file
View File

@@ -0,0 +1,18 @@
import { getCvCategories } from "~/server/db/query"
export default async function CvPage() {
const cvCategories = await getCvCategories();
return (
<div>
{cvCategories.map((category) => {
return (
<div key={category.id}>
<div>
{category.name}
</div>
</div>
)
})}
</div>
)
}

10
src/app/fun/layout.tsx Normal file
View File

@@ -0,0 +1,10 @@
'use client'
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode}>) {
return (
<>
{children}
</>
)
}

View File

@@ -1,11 +1,9 @@
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";
import { ClerkProvider } from "@clerk/nextjs";
import TopNav from "./_components/TopNav";
export const metadata: Metadata = {
title: "Gregor Lohaus",
description: "My Personal Website",
@@ -17,45 +15,18 @@ const geist = Geist({
variable: "--font-geist-sans",
});
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 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>
)
}
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
modal
}: Readonly<{ children: React.ReactNode, modal: React.ReactNode }>) {
return (
<ClerkProvider>
<html lang="en" className={`${geist.variable}`}>
<body className="flex flex-col gap-2 bg-black text-white">
<TopNav/>
{children}
{modal}
</body>
</html>
</ClerkProvider>

View File

@@ -1,6 +1,4 @@
export const dynamic = "force-dynamic"
export default async function HomePage() {
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center bg-black text-white">
<div>

View File

@@ -0,0 +1,10 @@
'use client'
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode}>) {
return (
<>
{children}
</>
)
}

View File

@@ -8,6 +8,24 @@ export const env = createEnv({
*/
server: {
DATABASE_URL: z.string().url(),
DATABASE_URL_UNPOOLED: z.string().url(),
PGHOST: z.string(),
PGHOST_UNPOOLED: z.string(),
PGUSER: z.string(),
PGDATABASE: z.string(),
PGPASSWORD: z.string(),
POSTGRES_URL: z.string().url(),
POSTGRES_URL_NON_POOLING: z.string().url(),
POSTGRES_USER: z.string(),
POSTGRES_HOST: z.string(),
POSTGRES_PASSWORD: z.string(),
POSTGRES_DATABASE: z.string(),
POSTGRES_URL_NO_SSL: z.string().url(),
POSTGRES_PRISMA_URL: z.string().url(),
CLERK_SECRET_KEY: z.string(),
ADMIN_USER_CLERK_ID: z.string(),
NODE_ENV: z
.enum(["development", "test", "production"])
@@ -20,6 +38,7 @@ export const env = createEnv({
* `NEXT_PUBLIC_`.
*/
client: {
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string()
// NEXT_PUBLIC_CLIENTVAR: z.string(),
},
@@ -29,9 +48,24 @@ export const env = createEnv({
*/
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
DATABASE_URL_UNPOOLED: process.env.DATABASE_URL,
PGHOST: process.env.PGHOST,
PGHOST_UNPOOLED: process.env.PGHOST_UNPOOLED,
PGUSER: process.env.PGUSER,
PGDATABASE: process.env.PGDATABASE,
PGPASSWORD: process.env.PGPASSWORD,
POSTGRES_URL: process.env.POSTGRES_URL,
POSTGRES_URL_NON_POOLING: process.env.POSTGRES_URL_NON_POOLING,
POSTGRES_USER: process.env.POSTGRES_USER,
POSTGRES_HOST: process.env.POSTGRES_HOST,
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
POSTGRES_URL_NO_SSL: process.env.POSTGRES_URL_NO_SSL,
POSTGRES_PRISMA_URL: process.env.POSTGRES_PRISMA_URL,
ADMIN_USER_CLERK_ID: process.env.ADMIN_USER_CLERK_ID,
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
NODE_ENV: process.env.NODE_ENV,
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially

View File

@@ -1 +1,9 @@
import "server-only"
import { db } from "."
export async function getCvCategories() {
const categories = await db.query.cvCategory.findMany({
orderBy: (model, {desc} ) => desc(model.name)
})
return categories;
}