admin Berreich mit Passwort
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ node_modules
|
|||||||
.wrangler
|
.wrangler
|
||||||
/.svelte-kit
|
/.svelte-kit
|
||||||
/build
|
/build
|
||||||
|
/data
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
Binary file not shown.
@@ -1,65 +1,60 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
let passwort = '';
|
let passwort = '';
|
||||||
let eingeloggt = false;
|
let eingeloggt = false;
|
||||||
let fehler = false;
|
let fehler = false;
|
||||||
let anmeldungen = [];
|
|
||||||
|
|
||||||
async function login() {
|
async function login() {
|
||||||
const res = await fetch('/api/admin/login', {
|
const res = await fetch('/api/admin/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ passwort }),
|
body: JSON.stringify({ passwort }),
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { 'Content-Type': 'application/json' }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
eingeloggt = true;
|
eingeloggt = true;
|
||||||
fehler = false;
|
fehler = false;
|
||||||
const result = await fetch('/api/admin/anmeldungen');
|
} else {
|
||||||
anmeldungen = await result.json();
|
fehler = true;
|
||||||
} else {
|
|
||||||
fehler = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="p-6 max-w-lg mx-auto">
|
||||||
<div class="p-6 max-w-4xl mx-auto">
|
{#if !eingeloggt}
|
||||||
{#if !eingeloggt}
|
<div class="space-y-4">
|
||||||
<div class="space-y-4">
|
<h1 class="text-2xl font-bold">Admin Login</h1>
|
||||||
<h1 class="text-2xl font-bold">Admin Login</h1>
|
<input type="password" bind:value={passwort} placeholder="Passwort" class="input w-full" />
|
||||||
<input type="password" bind:value={passwort} placeholder="Passwort" class="input w-full" />
|
<button on:click={login} class="bg-blue-600 text-white px-4 py-2 rounded">Login</button>
|
||||||
<button on:click={login} class="bg-blue-600 text-white px-4 py-2 rounded">Login</button>
|
{#if fehler}
|
||||||
{#if fehler}
|
<p class="text-red-600">Falsches Passwort</p>
|
||||||
<p class="text-red-600">Falsches Passwort</p>
|
{/if}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Admin-Bereich</h1>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<a href="/admin/anmeldungen" class="bg-blue-600 text-white px-4 py-3 rounded text-center hover:bg-blue-700">
|
||||||
|
📝 Anmeldungen anzeigen
|
||||||
|
</a>
|
||||||
|
<a href="/admin/dienststellen" class="bg-green-600 text-white px-4 py-3 rounded text-center hover:bg-green-700">
|
||||||
|
🏢 Dienststellen verwalten
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<button
|
||||||
{:else}
|
on:click={async () => {
|
||||||
<p><a href="/admin/dienststellen" class="text-blue-600 underline">Dienststellen verwalten</a></p>
|
await fetch('/api/admin/logout', { method: 'POST' });
|
||||||
<h1 class="text-2xl font-bold mb-4">Alle Anmeldungen</h1>
|
location.reload();
|
||||||
<table class="w-full border text-sm">
|
}}
|
||||||
<thead>
|
class="text-sm text-red-600 underline"
|
||||||
<tr class="bg-gray-200">
|
>
|
||||||
<th class="p-2 text-left">Name</th>
|
Logout
|
||||||
<th class="p-2 text-left">E-Mail</th>
|
</button>
|
||||||
<th class="p-2 text-left">Wunsch 1–3</th>
|
</div>
|
||||||
<th class="p-2 text-left">Datum</th>
|
{/if}
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#each anmeldungen as a}
|
|
||||||
<tr class="border-t">
|
|
||||||
<td class="p-2">{a.anrede} {a.vorname} {a.nachname}</td>
|
|
||||||
<td class="p-2">{a.email}</td>
|
|
||||||
<td class="p-2">{a.wunsch1}, {a.wunsch2}, {a.wunsch3}</td>
|
|
||||||
<td class="p-2">{new Date(a.timestamp).toLocaleDateString()}</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
|
<style>
|
||||||
<style>
|
.input {
|
||||||
.input {
|
@apply border rounded px-3 py-2 w-full;
|
||||||
@apply border rounded px-3 py-2 w-full;
|
}
|
||||||
}
|
</style>
|
||||||
8
src/routes/admin/anmeldungen/+page.server.ts
Normal file
8
src/routes/admin/anmeldungen/+page.server.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { PageServerLoad } from '../../api/admin/anmeldungen/$types';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ cookies }) => {
|
||||||
|
if (cookies.get('admin_session') !== 'true') {
|
||||||
|
throw redirect(303, '/admin');
|
||||||
|
}
|
||||||
|
};
|
||||||
40
src/routes/admin/anmeldungen/+page.svelte
Normal file
40
src/routes/admin/anmeldungen/+page.svelte
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
let anmeldungen = [];
|
||||||
|
|
||||||
|
async function ladeAnmeldungen() {
|
||||||
|
const res = await fetch('/api/admin/anmeldungen');
|
||||||
|
anmeldungen = await res.json();
|
||||||
|
}
|
||||||
|
onMount(ladeAnmeldungen);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="p-6 max-w-4xl mx-auto">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Alle Anmeldungen</h1>
|
||||||
|
<table class="w-full border text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="bg-gray-200">
|
||||||
|
<th class="p-2 text-left">Name</th>
|
||||||
|
<th class="p-2 text-left">E-Mail</th>
|
||||||
|
<th class="p-2 text-left">Wunsch 1–3</th>
|
||||||
|
<th class="p-2 text-left">Datum</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each anmeldungen as a}
|
||||||
|
<tr class="border-t">
|
||||||
|
<td class="p-2">{a.anrede} {a.vorname} {a.nachname}</td>
|
||||||
|
<td class="p-2">{a.email}</td>
|
||||||
|
<td class="p-2">{a.wunsch1.name}<br>{a.wunsch2.name}<br>{a.wunsch3.name}</td>
|
||||||
|
<td class="p-2">{new Date(a.timestamp).toLocaleDateString()}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.input {
|
||||||
|
@apply border rounded px-3 py-2 w-full;
|
||||||
|
}
|
||||||
8
src/routes/admin/dienststellen/+page.server.ts
Normal file
8
src/routes/admin/dienststellen/+page.server.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ cookies }) => {
|
||||||
|
if (cookies.get('admin_session') !== 'true') {
|
||||||
|
throw redirect(303, '/admin'); // zurück zur Login-Seite
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,11 +5,11 @@ const prisma = new PrismaClient();
|
|||||||
|
|
||||||
export const GET: RequestHandler = async () => {
|
export const GET: RequestHandler = async () => {
|
||||||
const anmeldungen = await prisma.anmeldung.findMany({
|
const anmeldungen = await prisma.anmeldung.findMany({
|
||||||
//include: {
|
include: {
|
||||||
// wunsch1: true,
|
wunsch1: true,
|
||||||
// wunsch2: true,
|
wunsch2: true,
|
||||||
// wunsch3: true
|
wunsch3: true
|
||||||
//};
|
},
|
||||||
orderBy: { timestamp: 'desc' }
|
orderBy: { timestamp: 'desc' }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => {
|
function checkAuth(cookies: any) {
|
||||||
|
return cookies.get('admin_session') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ cookies }) => {
|
||||||
|
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||||
const dienststellen = await prisma.dienststelle.findMany({ orderBy: { name: 'asc' } });
|
const dienststellen = await prisma.dienststelle.findMany({ orderBy: { name: 'asc' } });
|
||||||
return new Response(JSON.stringify(dienststellen));
|
return json(dienststellen);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ request }) => {
|
export const POST: RequestHandler = async ({ cookies, request }) => {
|
||||||
|
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||||
const { name } = await request.json();
|
const { name } = await request.json();
|
||||||
try {
|
try {
|
||||||
const created = await prisma.dienststelle.create({ data: { name } });
|
const created = await prisma.dienststelle.create({ data: { name } });
|
||||||
return new Response(JSON.stringify(created));
|
return json(created);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new Response(JSON.stringify({ error: 'Dienststelle existiert bereits' }), { status: 400 });
|
return json({ error: 'Dienststelle existiert bereits' }, { status: 400 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DELETE: RequestHandler = async ({ url }) => {
|
export const DELETE: RequestHandler = async ({ cookies, url }) => {
|
||||||
|
if (!checkAuth(cookies)) return new Response('Nicht erlaubt', { status: 401 });
|
||||||
const id = Number(url.searchParams.get('id'));
|
const id = Number(url.searchParams.get('id'));
|
||||||
await prisma.dienststelle.delete({ where: { id } });
|
await prisma.dienststelle.delete({ where: { id } });
|
||||||
return new Response(JSON.stringify({ success: true }));
|
return json({ success: true });
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
import { json } from '@sveltejs/kit';
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
const ADMIN_PASS = 'supergeheim'; // Passwort hier festlegen
|
const ADMIN_PASS = 'supergeheim'; // 🔒 Besser aus .env lesen
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ request }) => {
|
export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||||
const { passwort } = await request.json();
|
const { passwort } = await request.json();
|
||||||
|
|
||||||
if (passwort === ADMIN_PASS) {
|
if (passwort === ADMIN_PASS) {
|
||||||
return new Response(JSON.stringify({ success: true }));
|
cookies.set('admin_session', 'true', {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'strict',
|
||||||
|
maxAge: 60 * 60 * 4 // 4 Stunden
|
||||||
|
});
|
||||||
|
return json({ success: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response('Unauthorized', { status: 401 });
|
return json({ error: 'Falsches Passwort' }, { status: 401 });
|
||||||
};
|
};
|
||||||
6
src/routes/api/admin/logout/+server.ts
Normal file
6
src/routes/api/admin/logout/+server.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ cookies }) => {
|
||||||
|
cookies.delete('admin_session', { path: '/' });
|
||||||
|
return new Response('Ausgeloggt');
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user