diff --git a/.next/build-manifest.json b/.next/build-manifest.json index c3b20ec..323f908 100644 --- a/.next/build-manifest.json +++ b/.next/build-manifest.json @@ -27,10 +27,15 @@ "static/chunks/main.js", "static/chunks/pages/admin.js" ], - "/admin/login": [ + "/blog/[slug]": [ "static/chunks/webpack.js", "static/chunks/main.js", - "static/chunks/pages/admin/login.js" + "static/chunks/pages/blog/[slug].js" + ], + "/devlog": [ + "static/chunks/webpack.js", + "static/chunks/main.js", + "static/chunks/pages/devlog.js" ] }, "ampFirstPages": [] diff --git a/.next/cache/webpack/client-development/1.pack.gz b/.next/cache/webpack/client-development/1.pack.gz index c3f3117..3cfecbc 100644 Binary files a/.next/cache/webpack/client-development/1.pack.gz and b/.next/cache/webpack/client-development/1.pack.gz differ diff --git a/.next/cache/webpack/client-development/2.pack.gz b/.next/cache/webpack/client-development/2.pack.gz index b9ad86f..0001e57 100644 Binary files a/.next/cache/webpack/client-development/2.pack.gz and b/.next/cache/webpack/client-development/2.pack.gz differ diff --git a/.next/cache/webpack/client-development/3.pack.gz b/.next/cache/webpack/client-development/3.pack.gz index 5344e31..24e950e 100644 Binary files a/.next/cache/webpack/client-development/3.pack.gz and b/.next/cache/webpack/client-development/3.pack.gz differ diff --git a/.next/cache/webpack/client-development/4.pack.gz b/.next/cache/webpack/client-development/4.pack.gz index 2f83216..5b1e00d 100644 Binary files a/.next/cache/webpack/client-development/4.pack.gz and b/.next/cache/webpack/client-development/4.pack.gz differ diff --git a/.next/cache/webpack/client-development/5.pack.gz b/.next/cache/webpack/client-development/5.pack.gz index fe2200c..0b2e4cd 100644 Binary files a/.next/cache/webpack/client-development/5.pack.gz and b/.next/cache/webpack/client-development/5.pack.gz differ diff --git a/.next/cache/webpack/client-development/6.pack.gz b/.next/cache/webpack/client-development/6.pack.gz index 1d8dc6b..d5704e7 100644 Binary files a/.next/cache/webpack/client-development/6.pack.gz and b/.next/cache/webpack/client-development/6.pack.gz differ diff --git a/.next/cache/webpack/client-development/7.pack.gz b/.next/cache/webpack/client-development/7.pack.gz index 4ccadd4..88431c6 100644 Binary files a/.next/cache/webpack/client-development/7.pack.gz and b/.next/cache/webpack/client-development/7.pack.gz differ diff --git a/.next/cache/webpack/client-development/8.pack.gz b/.next/cache/webpack/client-development/8.pack.gz index 9ae6a99..ba23f74 100644 Binary files a/.next/cache/webpack/client-development/8.pack.gz and b/.next/cache/webpack/client-development/8.pack.gz differ diff --git a/.next/cache/webpack/client-development/index.pack.gz.old b/.next/cache/webpack/client-development/index.pack.gz.old index 7a72390..a9b92a9 100644 Binary files a/.next/cache/webpack/client-development/index.pack.gz.old and b/.next/cache/webpack/client-development/index.pack.gz.old differ diff --git a/.next/cache/webpack/server-development/1.pack.gz b/.next/cache/webpack/server-development/1.pack.gz index fca52c6..c8bd47d 100644 Binary files a/.next/cache/webpack/server-development/1.pack.gz and b/.next/cache/webpack/server-development/1.pack.gz differ diff --git a/.next/cache/webpack/server-development/4.pack.gz b/.next/cache/webpack/server-development/4.pack.gz index 69364af..586600e 100644 Binary files a/.next/cache/webpack/server-development/4.pack.gz and b/.next/cache/webpack/server-development/4.pack.gz differ diff --git a/.next/cache/webpack/server-development/5.pack.gz b/.next/cache/webpack/server-development/5.pack.gz index 5b23adf..537d999 100644 Binary files a/.next/cache/webpack/server-development/5.pack.gz and b/.next/cache/webpack/server-development/5.pack.gz differ diff --git a/.next/cache/webpack/server-development/6.pack.gz b/.next/cache/webpack/server-development/6.pack.gz index 43256db..282dfe7 100644 Binary files a/.next/cache/webpack/server-development/6.pack.gz and b/.next/cache/webpack/server-development/6.pack.gz differ diff --git a/.next/cache/webpack/server-development/7.pack.gz b/.next/cache/webpack/server-development/7.pack.gz index 386232d..9ba707b 100644 Binary files a/.next/cache/webpack/server-development/7.pack.gz and b/.next/cache/webpack/server-development/7.pack.gz differ diff --git a/.next/cache/webpack/server-development/8.pack.gz b/.next/cache/webpack/server-development/8.pack.gz index 088fae1..deac079 100644 Binary files a/.next/cache/webpack/server-development/8.pack.gz and b/.next/cache/webpack/server-development/8.pack.gz differ diff --git a/.next/cache/webpack/server-development/9.pack.gz b/.next/cache/webpack/server-development/9.pack.gz index 552622d..292065b 100644 Binary files a/.next/cache/webpack/server-development/9.pack.gz and b/.next/cache/webpack/server-development/9.pack.gz differ diff --git a/.next/cache/webpack/server-development/index.pack.gz b/.next/cache/webpack/server-development/index.pack.gz index 3b48659..6f0d274 100644 Binary files a/.next/cache/webpack/server-development/index.pack.gz and b/.next/cache/webpack/server-development/index.pack.gz differ diff --git a/.next/cache/webpack/server-development/index.pack.gz.old b/.next/cache/webpack/server-development/index.pack.gz.old index eb103f8..ddbf00e 100644 Binary files a/.next/cache/webpack/server-development/index.pack.gz.old and b/.next/cache/webpack/server-development/index.pack.gz.old differ diff --git a/.next/react-loadable-manifest.json b/.next/react-loadable-manifest.json index 9e26dfe..dde90fd 100644 --- a/.next/react-loadable-manifest.json +++ b/.next/react-loadable-manifest.json @@ -1 +1,18 @@ -{} \ No newline at end of file +{ + "..\\node_modules\\@supabase\\auth-js\\dist\\module\\lib\\helpers.js -> @supabase/node-fetch": { + "id": "..\\node_modules\\@supabase\\auth-js\\dist\\module\\lib\\helpers.js -> @supabase/node-fetch", + "files": [] + }, + "..\\node_modules\\@supabase\\functions-js\\dist\\module\\helper.js -> @supabase/node-fetch": { + "id": "..\\node_modules\\@supabase\\functions-js\\dist\\module\\helper.js -> @supabase/node-fetch", + "files": [] + }, + "..\\node_modules\\@supabase\\realtime-js\\dist\\module\\RealtimeClient.js -> @supabase/node-fetch": { + "id": "..\\node_modules\\@supabase\\realtime-js\\dist\\module\\RealtimeClient.js -> @supabase/node-fetch", + "files": [] + }, + "..\\node_modules\\@supabase\\storage-js\\dist\\module\\lib\\helpers.js -> @supabase/node-fetch": { + "id": "..\\node_modules\\@supabase\\storage-js\\dist\\module\\lib\\helpers.js -> @supabase/node-fetch", + "files": [] + } +} \ No newline at end of file diff --git a/.next/server/middleware-build-manifest.js b/.next/server/middleware-build-manifest.js index 9be1506..f0cc825 100644 --- a/.next/server/middleware-build-manifest.js +++ b/.next/server/middleware-build-manifest.js @@ -1 +1 @@ -self.__BUILD_MANIFEST={"polyfillFiles":["static/chunks/polyfills.js"],"devFiles":["static/chunks/react-refresh.js"],"ampDevFiles":[],"lowPriorityFiles":["static/development/_buildManifest.js","static/development/_ssgManifest.js"],"rootMainFiles":[],"pages":{"/_app":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/_app.js"],"/_error":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/_error.js"],"/admin":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/admin.js"],"/admin/login":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/admin/login.js"]},"ampFirstPages":[]} \ No newline at end of file +self.__BUILD_MANIFEST={"polyfillFiles":["static/chunks/polyfills.js"],"devFiles":["static/chunks/react-refresh.js"],"ampDevFiles":[],"lowPriorityFiles":["static/development/_buildManifest.js","static/development/_ssgManifest.js"],"rootMainFiles":[],"pages":{"/_app":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/_app.js"],"/_error":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/_error.js"],"/admin":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/admin.js"],"/blog/[slug]":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/blog/[slug].js"],"/devlog":["static/chunks/webpack.js","static/chunks/main.js","static/chunks/pages/devlog.js"]},"ampFirstPages":[]} \ No newline at end of file diff --git a/.next/server/middleware-react-loadable-manifest.js b/.next/server/middleware-react-loadable-manifest.js index ca34f09..19bcd68 100644 --- a/.next/server/middleware-react-loadable-manifest.js +++ b/.next/server/middleware-react-loadable-manifest.js @@ -1 +1 @@ -self.__REACT_LOADABLE_MANIFEST="{}" \ No newline at end of file +self.__REACT_LOADABLE_MANIFEST="{\"..\\\\node_modules\\\\@supabase\\\\auth-js\\\\dist\\\\module\\\\lib\\\\helpers.js -> @supabase/node-fetch\":{\"id\":\"..\\\\node_modules\\\\@supabase\\\\auth-js\\\\dist\\\\module\\\\lib\\\\helpers.js -> @supabase/node-fetch\",\"files\":[]},\"..\\\\node_modules\\\\@supabase\\\\functions-js\\\\dist\\\\module\\\\helper.js -> @supabase/node-fetch\":{\"id\":\"..\\\\node_modules\\\\@supabase\\\\functions-js\\\\dist\\\\module\\\\helper.js -> @supabase/node-fetch\",\"files\":[]},\"..\\\\node_modules\\\\@supabase\\\\realtime-js\\\\dist\\\\module\\\\RealtimeClient.js -> @supabase/node-fetch\":{\"id\":\"..\\\\node_modules\\\\@supabase\\\\realtime-js\\\\dist\\\\module\\\\RealtimeClient.js -> @supabase/node-fetch\",\"files\":[]},\"..\\\\node_modules\\\\@supabase\\\\storage-js\\\\dist\\\\module\\\\lib\\\\helpers.js -> @supabase/node-fetch\":{\"id\":\"..\\\\node_modules\\\\@supabase\\\\storage-js\\\\dist\\\\module\\\\lib\\\\helpers.js -> @supabase/node-fetch\",\"files\":[]}}" \ No newline at end of file diff --git a/.next/server/pages-manifest.json b/.next/server/pages-manifest.json index e208762..da3f17c 100644 --- a/.next/server/pages-manifest.json +++ b/.next/server/pages-manifest.json @@ -2,6 +2,7 @@ "/_app": "pages/_app.js", "/_error": "pages/_error.js", "/_document": "pages/_document.js", - "/admin/login": "pages/admin/login.js", - "/admin": "pages/admin.js" + "/admin": "pages/admin.js", + "/devlog": "pages/devlog.js", + "/blog/[slug]": "pages/blog/[slug].js" } \ No newline at end of file diff --git a/.next/static/development/_buildManifest.js b/.next/static/development/_buildManifest.js index 49ec8cc..8e176ab 100644 --- a/.next/static/development/_buildManifest.js +++ b/.next/static/development/_buildManifest.js @@ -1 +1 @@ -self.__BUILD_MANIFEST = {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static\u002Fchunks\u002Fpages\u002F_error.js"],"/admin":["static\u002Fchunks\u002Fpages\u002Fadmin.js"],"/admin/login":["static\u002Fchunks\u002Fpages\u002Fadmin\u002Flogin.js"],sortedPages:["\u002F_app","\u002F_error","\u002Fadmin","\u002Fadmin\u002Flogin"]};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB() \ No newline at end of file +self.__BUILD_MANIFEST = {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static\u002Fchunks\u002Fpages\u002F_error.js"],"/admin":["static\u002Fchunks\u002Fpages\u002Fadmin.js"],"/blog/[slug]":["static\u002Fchunks\u002Fpages\u002Fblog\u002F[slug].js"],"/devlog":["static\u002Fchunks\u002Fpages\u002Fdevlog.js"],sortedPages:["\u002F_app","\u002F_error","\u002Fadmin","\u002Fblog\u002F[slug]","\u002Fdevlog"]};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB() \ No newline at end of file diff --git a/components/AdminLayout.js b/components/AdminLayout.js new file mode 100644 index 0000000..614309f --- /dev/null +++ b/components/AdminLayout.js @@ -0,0 +1,53 @@ +import { useRouter } from 'next/router'; +import { useState } from 'react'; + +const AdminLayout = ({ children }) => { + const router = useRouter(); + const [isLoggingOut, setIsLoggingOut] = useState(false); + + const handleLogout = async () => { + setIsLoggingOut(true); + try { + // Cookie löschen + document.cookie = 'isAuthenticated=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; + + // Zur Login-Seite weiterleiten + router.push('/admin/login'); + } catch (error) { + console.error('Logout-Fehler:', error); + } finally { + setIsLoggingOut(false); + } + }; + + return ( +
+ {/* Admin Header */} +
+
+
+

+ Admin Panel +

+ +
+
+
+ + {/* Main Content */} +
+
+ {children} +
+
+
+ ); +}; + +export default AdminLayout; \ No newline at end of file diff --git a/components/LoginForm.js b/components/LoginForm.js index 64f4a13..8108cb6 100644 --- a/components/LoginForm.js +++ b/components/LoginForm.js @@ -21,7 +21,7 @@ export default function LoginForm() { const data = await res.json(); if (data.success) { - router.reload(); + router.push('/admin'); } else { setError('Ungültige Zugangsdaten'); } diff --git a/components/Navbar.js b/components/Navbar.js index 0c1da3e..8dfd054 100644 --- a/components/Navbar.js +++ b/components/Navbar.js @@ -25,6 +25,7 @@ export default function Navbar() { alt="Pixelbrew Logo" width={300} height={300} + priority style={{ marginTop: '4rem', marginBottom: '1rem' }} /> diff --git a/lib/supabase.js b/lib/supabase.js new file mode 100644 index 0000000..d0049ed --- /dev/null +++ b/lib/supabase.js @@ -0,0 +1,11 @@ +import { createClient } from '@supabase/supabase-js' + +const supabaseUrl = 'https://dlwcsisbvxvlmfbzjszs.supabase.co' +const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRsd2NzaXNidnh2bG1mYnpqc3pzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTYyMTQzODMsImV4cCI6MjA3MTc5MDM4M30.xJGPs921Ta5gSLvMuCpAwOVSL9DfQpJeruhgH1350vU' +const supabaseServiceKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRsd2NzaXNidnh2bG1mYnpqc3pzIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NjIxNDM4MywiZXhwIjoyMDcxNzkwMzgzfQ.jOaOmZkk6MLzpyHeshtEG5RTEzBUfnMe1Riij6cfOg0' + +// Client for frontend (anon key) +export const supabase = createClient(supabaseUrl, supabaseAnonKey) + +// Client for API routes (service role key) +export const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cb8ed6c..38d5f05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@heroicons/react": "^2.2.0", + "@supabase/supabase-js": "^2.56.0", "axios": "^1.11.0", "framer-motion": "^12.16.0", "leaflet": "^1.9.4", @@ -378,6 +379,80 @@ "react-dom": "^18.0.0" } }, + "node_modules/@supabase/auth-js": { + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", + "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", + "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.21.3.tgz", + "integrity": "sha512-rg3DmmZQKEVCreXq6Am29hMVe1CzemXyIWVYyyua69y6XubfP+DzGfLxME/1uvdgwqdoaPbtjBDpEBhqxq1ZwA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.1.tgz", + "integrity": "sha512-edRFa2IrQw50kNntvUyS38hsL7t2d/psah6om6aNTLLcWem0R6bOUq7sk7DsGeSlNfuwEwWn57FdYSva6VddYw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.11.0.tgz", + "integrity": "sha512-Y+kx/wDgd4oasAgoAq0bsbQojwQ+ejIif8uczZ9qufRHWFLMU5cODT+ApHsSrDufqUcVKt+eyxtOXSkeh2v9ww==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.56.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.56.0.tgz", + "integrity": "sha512-XqwhHSyVnkjdliPN61CmXsmFGnFHTX2WDdwjG3Ukvdzuu3Trix+dXupYOQ3BueIyYp7B6t0yYpdQtJP2hIInyg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.71.1", + "@supabase/functions-js": "2.4.5", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.21.3", + "@supabase/realtime-js": "2.15.1", + "@supabase/storage-js": "^2.10.4" + } + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -391,12 +466,17 @@ "version": "22.15.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz", "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.6", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz", @@ -407,6 +487,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2234,6 +2323,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -2265,7 +2360,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/update-browserslist-db": { @@ -2319,6 +2413,22 @@ "node": ">=10.13.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2432,6 +2542,27 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yaml": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", diff --git a/package.json b/package.json index 63fe58b..8a90844 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@heroicons/react": "^2.2.0", + "@supabase/supabase-js": "^2.56.0", "axios": "^1.11.0", "framer-motion": "^12.16.0", "leaflet": "^1.9.4", diff --git a/pages/_app.js b/pages/_app.js index 7c93212..abd1bcf 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,8 +1,23 @@ +import { useRouter } from 'next/router'; import Layout from '../components/Layout'; +import AdminLayout from '../components/AdminLayout'; import CookieBanner from '../components/CookieBanner'; import '../styles/globals.css'; function MyApp({ Component, pageProps }) { + const router = useRouter(); + + // Prüfen ob es sich um eine Admin-Seite handelt + const isAdminPage = router.pathname.startsWith('/admin'); + + if (isAdminPage) { + return ( + + + + ); + } + return ( diff --git a/pages/admin/index.js b/pages/admin/index.js index 3c6ed23..4c4cbd3 100644 --- a/pages/admin/index.js +++ b/pages/admin/index.js @@ -1,8 +1,9 @@ -import { useState } from 'react'; -import devlogData from '../../data/devlog.json'; +import { useState, useEffect } from 'react'; +import { supabase } from '../../lib/supabase'; export default function AdminPanel() { - const [entries, setEntries] = useState(devlogData); + const [entries, setEntries] = useState([]); + const [loading, setLoading] = useState(true); const [selectedEntry, setSelectedEntry] = useState(null); const emptyEntry = { date: new Date().toLocaleDateString('de-DE'), @@ -13,15 +14,42 @@ export default function AdminPanel() { content: [] }; + // Load data from Supabase on component mount + useEffect(() => { + loadEntries(); + }, []); + + const loadEntries = async () => { + try { + const { data, error } = await supabase + .from('devlog_entries') + .select('*') + .order('created_at', { ascending: false }); + + if (error) throw error; + + setEntries(data || []); + } catch (error) { + console.error('Fehler beim Laden der Einträge:', error); + alert('Fehler beim Laden der Daten'); + } finally { + setLoading(false); + } + }; + const handleSave = async (entry, index = null) => { - // Entferne das 'index' Feld aus dem Entry bevor es gespeichert wird + // Entferne das 'index' und 'editIndex' Feld aus dem Entry const cleanEntry = { ...entry }; delete cleanEntry.index; + delete cleanEntry.editIndex; + delete cleanEntry.id; // Remove id for new entries let updatedEntries; if (index !== null) { - // Bearbeite bestehenden Eintrag + // Bearbeite bestehenden Eintrag - keep the original id + const originalEntry = entries[index]; + cleanEntry.id = originalEntry.id; updatedEntries = entries.map((e, i) => i === index ? cleanEntry : e); } else { // Füge neuen Eintrag hinzu @@ -37,7 +65,8 @@ export default function AdminPanel() { if (!response.ok) throw new Error('Fehler beim Speichern'); - setEntries(updatedEntries); + // Reload data from Supabase to get updated entries with IDs + await loadEntries(); setSelectedEntry(null); } catch (error) { console.error('Fehler:', error); @@ -153,6 +182,17 @@ export default function AdminPanel() { ); }; + if (loading) { + return ( +
+

DevLog Admin

+
+
Lade Daten...
+
+
+ ); + } + return (

DevLog Admin

diff --git a/pages/api/update-devlog.js b/pages/api/update-devlog.js index e811c06..1c39a3f 100644 --- a/pages/api/update-devlog.js +++ b/pages/api/update-devlog.js @@ -1,75 +1,75 @@ -import fs from 'fs'; -import path from 'path'; +import { supabaseAdmin } from '../../lib/supabase'; -export default function handler(req, res) { - console.log('API /update-devlog aufgerufen'); - console.log('Request method:', req.method); - console.log('Request body:', JSON.stringify(req.body, null, 2)); - - const filePath = path.join(process.cwd(), 'data', 'devlog.json'); - console.log('File path:', filePath); +export default async function handler(req, res) { + console.log('=== UPDATE DEVLOG API CALLED (Supabase) ==='); + console.log('Method:', req.method); + console.log('Body:', JSON.stringify(req.body, null, 2)); - if (req.method === 'POST') { - try { - // Validiere eingehende Daten - if (!req.body) { - console.error('Fehler: Kein Request Body vorhanden'); - return res.status(400).json({ message: 'Kein Request Body vorhanden' }); - } - - if (!Array.isArray(req.body)) { - console.error('Fehler: Request Body ist kein Array'); - return res.status(400).json({ message: 'Request Body muss ein Array sein' }); - } - - console.log('Anzahl der Einträge:', req.body.length); - - // Prüfe Dateiberechtigungen - try { - fs.accessSync(path.dirname(filePath), fs.constants.W_OK); - console.log('Schreibberechtigung für Verzeichnis vorhanden'); - } catch (permError) { - console.error('Fehler: Keine Schreibberechtigung für Verzeichnis:', permError.message); - return res.status(500).json({ message: 'Keine Schreibberechtigung für Verzeichnis', error: permError.message }); - } - - // Stelle sicher, dass jeder Eintrag ein content-Array hat und entferne unerwünschte Felder - const updatedData = req.body.map((entry, index) => { - console.log(`Verarbeite Eintrag ${index}:`, entry); - - // Validiere erforderliche Felder - if (!entry.title || !entry.date) { - console.error(`Fehler: Eintrag ${index} fehlen erforderliche Felder (title oder date)`); - throw new Error(`Eintrag ${index} fehlen erforderliche Felder`); - } - - // Entferne unerwünschte Felder wie editIndex, index etc. - const { editIndex, index: entryIndex, ...cleanEntry } = entry; - - return { - ...cleanEntry, - content: cleanEntry.content || [] - }; - }); - - console.log('Verarbeitete Daten:', JSON.stringify(updatedData, null, 2)); - - // Schreibe Datei - fs.writeFileSync(filePath, JSON.stringify(updatedData, null, 2), 'utf8'); - console.log('Datei erfolgreich geschrieben'); - - res.status(200).json({ message: 'DevLog updated successfully!' }); - } catch (error) { - console.error('Fehler beim Verarbeiten der Anfrage:', error); - console.error('Error stack:', error.stack); - res.status(500).json({ - message: 'Error updating DevLog', - error: error.message, - stack: process.env.NODE_ENV === 'development' ? error.stack : undefined - }); + if (req.method !== 'POST') { + console.log('Method not allowed:', req.method); + return res.status(405).json({ error: 'Method not allowed' }); + } + + try { + const data = req.body; + console.log('Received data type:', typeof data); + console.log('Is array:', Array.isArray(data)); + + // Validate that data is an array + if (!Array.isArray(data)) { + console.log('Invalid data format - not an array'); + return res.status(400).json({ error: 'Data must be an array' }); } - } else { - console.log('Methode nicht erlaubt:', req.method); - res.status(405).json({ message: 'Method not allowed' }); + + // Process each entry for Supabase + const results = []; + + for (const entry of data) { + // Ensure content is an array + const content = Array.isArray(entry.content) ? entry.content : []; + + // Create clean entry with required fields + const cleanEntry = { + title: entry.title || '', + date: entry.date || '', + description: entry.description || '', + slug: entry.slug || '', + image: entry.image || '', + content: content, + updated_at: new Date().toISOString() + }; + + console.log('Processing entry:', cleanEntry); + + // Use UPSERT (insert or update based on slug) + const { data: result, error } = await supabaseAdmin + .from('devlog_entries') + .upsert(cleanEntry, { + onConflict: 'slug', + returning: 'minimal' + }); + + if (error) { + console.error('Supabase upsert error:', error); + throw error; + } + + results.push(result); + console.log('Entry upserted successfully:', cleanEntry.slug); + } + + console.log('All entries processed successfully'); + res.status(200).json({ + message: 'DevLog updated successfully in Supabase', + count: data.length + }); + + } catch (error) { + console.error('Error updating devlog in Supabase:', error); + res.status(500).json({ + error: 'Failed to update devlog in Supabase', + details: error.message, + stack: error.stack + }); } } diff --git a/pages/devlog.js b/pages/devlog.js index 02d69f4..46f0910 100644 --- a/pages/devlog.js +++ b/pages/devlog.js @@ -1,9 +1,15 @@ import { motion } from 'framer-motion'; import Link from 'next/link'; -import devlogData from '../data/devlog.json'; import Image from 'next/image'; +import { supabase } from '../lib/supabase'; -export default function DevLog() { +// Funktion zur Validierung von Bildpfaden +const isValidImagePath = (imagePath) => { + if (!imagePath || typeof imagePath !== 'string') return false; + return imagePath.startsWith('/') || imagePath.startsWith('http://') || imagePath.startsWith('https://'); +}; + +export default function DevLog({ devlogData }) { return (
- {devlogData.map((log, index) => ( + {devlogData && devlogData.map((log, index) => ( - {log.image && ( + {log.image && isValidImagePath(log.image) && (
{log.title}
@@ -58,3 +65,34 @@ export default function DevLog() {
); } + +export async function getServerSideProps() { + try { + const { data: devlogData, error } = await supabase + .from('devlog_entries') + .select('*') + .order('created_at', { ascending: false }); + + if (error) { + console.error('Error fetching devlog data:', error); + return { + props: { + devlogData: [] + } + }; + } + + return { + props: { + devlogData: devlogData || [] + } + }; + } catch (error) { + console.error('Error in getServerSideProps:', error); + return { + props: { + devlogData: [] + } + }; + } +} diff --git a/supabase/migrations/create_devlog_table.sql b/supabase/migrations/create_devlog_table.sql new file mode 100644 index 0000000..6ee07ec --- /dev/null +++ b/supabase/migrations/create_devlog_table.sql @@ -0,0 +1,34 @@ +-- Create devlog_entries table +CREATE TABLE devlog_entries ( + id SERIAL PRIMARY KEY, + date TEXT NOT NULL, + title TEXT NOT NULL, + description TEXT, + slug TEXT UNIQUE NOT NULL, + image TEXT, + content JSONB DEFAULT '[]'::jsonb, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Enable Row Level Security +ALTER TABLE devlog_entries ENABLE ROW LEVEL SECURITY; + +-- Create policies for public read access +CREATE POLICY "Allow public read access" ON devlog_entries + FOR SELECT USING (true); + +-- Create policies for authenticated users to insert/update +CREATE POLICY "Allow authenticated insert" ON devlog_entries + FOR INSERT WITH CHECK (true); + +CREATE POLICY "Allow authenticated update" ON devlog_entries + FOR UPDATE USING (true); + +CREATE POLICY "Allow authenticated delete" ON devlog_entries + FOR DELETE USING (true); + +-- Grant permissions to anon and authenticated roles +GRANT SELECT ON devlog_entries TO anon; +GRANT ALL PRIVILEGES ON devlog_entries TO authenticated; +GRANT USAGE, SELECT ON SEQUENCE devlog_entries_id_seq TO authenticated; \ No newline at end of file