umstieg auf supabase
This commit is contained in:
parent
3797c83c28
commit
00af25d9d6
@ -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": []
|
||||
|
||||
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/2.pack.gz
vendored
BIN
.next/cache/webpack/client-development/2.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/3.pack.gz
vendored
BIN
.next/cache/webpack/client-development/3.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/4.pack.gz
vendored
BIN
.next/cache/webpack/client-development/4.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/5.pack.gz
vendored
BIN
.next/cache/webpack/client-development/5.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/6.pack.gz
vendored
BIN
.next/cache/webpack/client-development/6.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/7.pack.gz
vendored
BIN
.next/cache/webpack/client-development/7.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/8.pack.gz
vendored
BIN
.next/cache/webpack/client-development/8.pack.gz
vendored
Binary file not shown.
Binary file not shown.
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/4.pack.gz
vendored
BIN
.next/cache/webpack/server-development/4.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/5.pack.gz
vendored
BIN
.next/cache/webpack/server-development/5.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/6.pack.gz
vendored
BIN
.next/cache/webpack/server-development/6.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/7.pack.gz
vendored
BIN
.next/cache/webpack/server-development/7.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/8.pack.gz
vendored
BIN
.next/cache/webpack/server-development/8.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/9.pack.gz
vendored
BIN
.next/cache/webpack/server-development/9.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Binary file not shown.
Binary file not shown.
@ -1 +1,18 @@
|
||||
{}
|
||||
{
|
||||
"..\\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": []
|
||||
}
|
||||
}
|
||||
@ -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":[]}
|
||||
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":[]}
|
||||
@ -1 +1 @@
|
||||
self.__REACT_LOADABLE_MANIFEST="{}"
|
||||
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\":[]}}"
|
||||
@ -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"
|
||||
}
|
||||
@ -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()
|
||||
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()
|
||||
53
components/AdminLayout.js
Normal file
53
components/AdminLayout.js
Normal file
@ -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 (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Admin Header */}
|
||||
<header className="bg-white shadow-sm border-b">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<h1 className="text-xl font-semibold text-gray-900">
|
||||
Admin Panel
|
||||
</h1>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
disabled={isLoggingOut}
|
||||
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isLoggingOut ? 'Wird abgemeldet...' : 'Abmelden'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||
<div className="px-4 py-6 sm:px-0">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminLayout;
|
||||
@ -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');
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ export default function Navbar() {
|
||||
alt="Pixelbrew Logo"
|
||||
width={300}
|
||||
height={300}
|
||||
priority
|
||||
style={{ marginTop: '4rem', marginBottom: '1rem' }}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
11
lib/supabase.js
Normal file
11
lib/supabase.js
Normal file
@ -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)
|
||||
135
package-lock.json
generated
135
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 (
|
||||
<AdminLayout>
|
||||
<Component {...pageProps} />
|
||||
</AdminLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
|
||||
@ -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 (
|
||||
<div className="container mx-auto p-8">
|
||||
<h1 className="text-3xl font-bold mb-8">DevLog Admin</h1>
|
||||
<div className="flex justify-center items-center h-64">
|
||||
<div className="text-xl">Lade Daten...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-8">
|
||||
<h1 className="text-3xl font-bold mb-8">DevLog Admin</h1>
|
||||
|
||||
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
@ -24,7 +30,7 @@ export default function DevLog() {
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{devlogData.map((log, index) => (
|
||||
{devlogData && devlogData.map((log, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@ -32,12 +38,13 @@ export default function DevLog() {
|
||||
transition={{ delay: index * 0.2 }}
|
||||
className="relative p-8 bg-white before:content-[''] before:absolute before:top-0 before:left-0 before:w-8 before:h-8 before:border-t-2 before:border-l-2 before:border-pb-turquoise after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-8 after:h-8 after:border-b-2 after:border-r-2 after:border-pb-turquoise"
|
||||
>
|
||||
{log.image && (
|
||||
{log.image && isValidImagePath(log.image) && (
|
||||
<div className="relative w-full h-48 mb-4 overflow-hidden">
|
||||
<Image
|
||||
src={log.image}
|
||||
alt={log.title}
|
||||
fill
|
||||
priority={index < 2}
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
@ -58,3 +65,34 @@ export default function DevLog() {
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
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: []
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
34
supabase/migrations/create_devlog_table.sql
Normal file
34
supabase/migrations/create_devlog_table.sql
Normal file
@ -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;
|
||||
Loading…
x
Reference in New Issue
Block a user