cleanup
This commit is contained in:
4
src/app/@modal/default.tsx
Normal file
4
src/app/@modal/default.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
export default function Default() {
|
||||
return null
|
||||
}
|
||||
|
||||
9
src/app/_components/AdminWrap.tsx
Normal file
9
src/app/_components/AdminWrap.tsx
Normal 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 (<></>)
|
||||
}
|
||||
|
||||
25
src/app/_components/TopNav.tsx
Normal file
25
src/app/_components/TopNav.tsx
Normal 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
8
src/app/actions.ts
Normal 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)
|
||||
}
|
||||
@@ -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
10
src/app/blog/layout.tsx
Normal 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
10
src/app/cv/layout.tsx
Normal 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
18
src/app/cv/page.tsx
Normal 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
10
src/app/fun/layout.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
'use client'
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode}>) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
10
src/app/projects/layout.tsx
Normal file
10
src/app/projects/layout.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
'use client'
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode}>) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
36
src/env.js
36
src/env.js
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user