diff --git a/.gitignore b/.gitignore index c24a835..9cbb774 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,6 @@ yarn-error.log* *.tsbuildinfo # idea files -.idea \ No newline at end of file +.idea +# clerk configuration (can include secrets) +/.clerk/ diff --git a/package.json b/package.json index 1781c17..5f75851 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@clerk/nextjs": "^6.14.0", "@neondatabase/serverless": "^1.0.0", "@t3-oss/env-nextjs": "^0.12.0", "drizzle-orm": "^0.41.0", @@ -25,6 +26,7 @@ "postgres": "^3.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", + "server-only": "^0.0.1", "zod": "^3.24.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecb4e3e..f92c40b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@clerk/nextjs': + specifier: ^6.14.0 + version: 6.14.0(next@15.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@neondatabase/serverless': specifier: ^1.0.0 version: 1.0.0 @@ -29,6 +32,9 @@ importers: react-dom: specifier: ^19.0.0 version: 19.1.0(react@19.1.0) + server-only: + specifier: ^0.0.1 + version: 0.0.1 zod: specifier: ^3.24.2 version: 3.24.2 @@ -120,6 +126,46 @@ packages: cpu: [x64] os: [win32] + '@clerk/backend@1.27.0': + resolution: {integrity: sha512-WqdigkqGyVcU0Y08xCJSor6I8j7WxtlZfMlIbHyw+JM8U06kNVSX9umRmTePFRFN02EwIQmQmqnRXVn0kwMyBQ==} + engines: {node: '>=18.17.0'} + peerDependencies: + svix: ^1.62.0 + peerDependenciesMeta: + svix: + optional: true + + '@clerk/clerk-react@5.26.0': + resolution: {integrity: sha512-9PQfCSFvpQDtL5fcp/gnQHBKvkMSW/FTuIEHdzhqhcO8xJRcOmcE0z5YjrnhytB3j3P1fx2gdiJgO5/OaU33hg==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + + '@clerk/nextjs@6.14.0': + resolution: {integrity: sha512-kSb0yPLbaACyRcvGDB2T0/IyypNYBQMIUP95YhFrZZWJDKR96rUdzw5ptG/OaP+vw7fAyhfz3Q8OeMrn97hxBw==} + engines: {node: '>=18.17.0'} + peerDependencies: + next: ^13.5.7 || ^14.2.25 || ^15.2.3 + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + + '@clerk/shared@3.4.0': + resolution: {integrity: sha512-3ms1JZVNXe/qJXdsFY+QlOYKcocdMKDHXDr/EvpuQV+WsLKkpMcswH2fbWNhsWG2otekL2sIYBVkCy+/0hBlPA==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@clerk/types@4.51.0': + resolution: {integrity: sha512-igUZ5BOe3rOgQSM8v9f6zHCOowX3yV36zWYImMUWD61cpLqUokKtN6qH2x8sHUHrq3B9Dj3AsXItGaQsLNs3mw==} + engines: {node: '>=18.17.0'} + '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -721,6 +767,10 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -733,10 +783,17 @@ packages: supports-color: optional: true + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + drizzle-kit@0.30.6: resolution: {integrity: sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g==} hasBin: true @@ -861,6 +918,9 @@ packages: get-tsconfig@4.10.0: resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -875,6 +935,10 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + lightningcss-darwin-arm64@1.29.2: resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} engines: {node: '>= 12.0.0'} @@ -939,6 +1003,13 @@ packages: resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} engines: {node: '>= 12.0.0'} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -968,6 +1039,9 @@ packages: sass: optional: true + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} @@ -1040,6 +1114,9 @@ packages: engines: {node: '>=10'} hasBin: true + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1051,6 +1128,13 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + snakecase-keys@8.0.1: + resolution: {integrity: sha512-Sj51kE1zC7zh6TDlNNz0/Jn1n5HiHdoQErxO8jLtnyrkJW/M5PrI7x05uDgY3BO7OUQYKCvmeMurW6BPUdwEOw==} + engines: {node: '>=18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1062,6 +1146,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -1079,6 +1166,11 @@ packages: babel-plugin-macros: optional: true + swr@2.3.3: + resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + tailwindcss@4.1.3: resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==} @@ -1089,6 +1181,10 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-fest@4.39.1: + resolution: {integrity: sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==} + engines: {node: '>=16'} + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -1100,6 +1196,11 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + which@4.0.0: resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} engines: {node: ^16.13.0 || >=18.0.0} @@ -1147,6 +1248,55 @@ snapshots: '@biomejs/cli-win32-x64@1.9.4': optional: true + '@clerk/backend@1.27.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@clerk/shared': 3.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.51.0 + cookie: 1.0.2 + snakecase-keys: 8.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - react + - react-dom + + '@clerk/clerk-react@5.26.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@clerk/shared': 3.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.51.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + tslib: 2.8.1 + + '@clerk/nextjs@6.14.0(next@15.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@clerk/backend': 1.27.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/clerk-react': 5.26.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/shared': 3.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@clerk/types': 4.51.0 + next: 15.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + server-only: 0.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - svix + + '@clerk/shared@3.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@clerk/types': 4.51.0 + dequal: 2.0.3 + glob-to-regexp: 0.4.1 + js-cookie: 3.0.5 + std-env: 3.9.0 + swr: 2.3.3(react@19.1.0) + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@clerk/types@4.51.0': + dependencies: + csstype: 3.1.3 + '@drizzle-team/brocli@0.10.2': {} '@emnapi/runtime@1.4.0': @@ -1539,14 +1689,23 @@ snapshots: color-string: 1.9.1 optional: true + cookie@1.0.2: {} + csstype@3.1.3: {} debug@4.4.0: dependencies: ms: 2.1.3 + dequal@2.0.3: {} + detect-libc@2.0.3: {} + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + drizzle-kit@0.30.6: dependencies: '@drizzle-team/brocli': 0.10.2 @@ -1644,6 +1803,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob-to-regexp@0.4.1: {} + graceful-fs@4.2.11: {} is-arrayish@0.3.2: @@ -1653,6 +1814,8 @@ snapshots: jiti@2.4.2: {} + js-cookie@3.0.5: {} + lightningcss-darwin-arm64@1.29.2: optional: true @@ -1698,6 +1861,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.29.2 lightningcss-win32-x64-msvc: 1.29.2 + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + map-obj@4.3.0: {} + ms@2.1.3: {} nanoid@3.3.11: {} @@ -1727,6 +1896,11 @@ snapshots: - '@babel/core' - babel-plugin-macros + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + obuf@1.1.2: {} pg-int8@1.0.1: {} @@ -1786,6 +1960,8 @@ snapshots: semver@7.7.1: {} + server-only@0.0.1: {} + sharp@0.33.5: dependencies: color: 4.2.3 @@ -1820,6 +1996,17 @@ snapshots: is-arrayish: 0.3.2 optional: true + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + snakecase-keys@8.0.1: + dependencies: + map-obj: 4.3.0 + snake-case: 3.0.4 + type-fest: 4.39.1 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -1829,6 +2016,8 @@ snapshots: source-map@0.6.1: {} + std-env@3.9.0: {} + streamsearch@1.1.0: {} styled-jsx@5.1.6(react@19.1.0): @@ -1836,18 +2025,30 @@ snapshots: client-only: 0.0.1 react: 19.1.0 + swr@2.3.3(react@19.1.0): + dependencies: + dequal: 2.0.3 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + tailwindcss@4.1.3: {} tapable@2.2.1: {} tslib@2.8.1: {} + type-fest@4.39.1: {} + typescript@5.8.3: {} undici-types@6.19.8: {} undici-types@6.21.0: {} + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + which@4.0.0: dependencies: isexe: 3.1.1 diff --git a/public/GLIcon.svg b/public/GLIcon.svg new file mode 100644 index 0000000..70c733d --- /dev/null +++ b/public/GLIcon.svg @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx new file mode 100644 index 0000000..cd044d9 --- /dev/null +++ b/src/app/admin/page.tsx @@ -0,0 +1,15 @@ +import { SignedIn } from "@clerk/nextjs"; + +const AdminPage = async () => { + return ( + +
+
+ hello admin +
+
+
+ ) +} + +export default AdminPage; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4cac50c..8b613d2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -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 ( -