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 */}
+
+
+ {/* 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 (
+
+ );
+ }
+
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) && (
@@ -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