commit d3367f0046312233bea9109487ba4b5b11e92cdf Author: lo Date: Mon Apr 13 23:17:07 2026 +0200 Initial commit: FL-Akademie LMS mit Docker, Admin, Portal und Dokumentation. Made-with: Cursor diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..462d3f3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +.next +.git +.env +.env.* +!.env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ea36120 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +DATABASE_URL="postgresql://akademie:devsecret@localhost:5433/akademie" +NEXTAUTH_URL="http://localhost:3000" +NEXTAUTH_SECRET="replace-with-random-32-chars-minimum-in-production" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..86e7f2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +.next +.env +.env.local +*.log +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b230bf0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM node:20-bookworm-slim + +WORKDIR /app + +RUN apt-get update -y && apt-get install -y openssl ca-certificates && rm -rf /var/lib/apt/lists/* + +COPY package.json ./ +RUN npm install --ignore-scripts + +COPY . . +RUN npx prisma generate + +RUN chmod +x scripts/docker-entrypoint.sh + +EXPOSE 3000 + +ENTRYPOINT ["/app/scripts/docker-entrypoint.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bbc116 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# FL-Akademie (Motorrad-Akademie LMS) + +Private Lernplattform als **Ersatz für Tutor LMS** für die [Fahrlässig Motorrad Akademie](https://akademie.fahrlaessig.com/) – Next.js, PostgreSQL, Prisma, NextAuth. Ziel: eigene Kontrolle über Kurse, Nutzer, Landing Page und Zertifikate ohne WordPress-Plugin-Abhängigkeit. + +**Remote-Repository:** `https://git.loepperts.com/loepperts/FL-Akademie.git` + +--- + +## Schnellstart (Docker) + +Voraussetzung: Docker & Docker Compose. + +```bash +git clone https://git.loepperts.com/loepperts/FL-Akademie.git +cd FL-Akademie +docker compose up --build -d +``` + +- **App:** [http://localhost:3000](http://localhost:3000) +- **PostgreSQL (vom Host):** `localhost:5433` → Container-Port 5432 (weniger Konflikte mit lokalem Postgres) + +Beim Start führt der Web-Container aus: `prisma generate`, `prisma migrate deploy`, `prisma db seed`, danach **`next dev`**. + +--- + +## Umgebungsvariablen + +Siehe `.env.example`. In `docker-compose.yml` sind für die Entwicklung bereits Werte gesetzt (`DATABASE_URL`, `NEXTAUTH_URL`, `NEXTAUTH_SECRET`). Für Produktion **eigenes starkes `NEXTAUTH_SECRET`** und korrekte **`NEXTAUTH_URL`** (öffentliche HTTPS-URL) setzen. + +--- + +## Demo-Zugänge (Seed) + +| Rolle | E-Mail | Passwort | +|------------|---------------------------|--------------| +| Admin | `admin@akademie.local` | `devpassword` | +| Dozent | `matze@akademie.local` | `devpassword` | +| Lernender | `lernender@akademie.local`| `devpassword` | + +Der Lernende ist im Demo-Kurs „Modul 1 – Die Fahrschule“ eingeschrieben. + +--- + +## Wichtige Routen + +| Bereich | Pfad | Beschreibung | +|--------|------|----------------| +| Öffentlich | `/` | Startseite (Inhalte aus DB, bearbeitbar im Admin) | +| Kurse | `/kurse`, `/kurse/[slug]` | Katalog, Kurssdetail, Einschreibung (kostenlose Kurse) | +| Lektionen | `/kurse/[slug]/lektionen/[lessonSlug]` | Lernansicht (Login + Einschreibung nötig) | +| Login | `/login` | Anmeldung | +| Mitgliederbereich | `/portal` | Fortschritt, Kurse wiederholen, Link zu Zertifikaten | +| Konto | `/portal/account` | Passwort ändern | +| Zertifikate (Liste) | `/portal/certificates` | Eigene Teilnahmebestätigungen | +| Zertifikat (öffentlich) | `/zertifikat/[code]` | Anzeige/Druck mit Verifikationscode | +| Administration | `/admin` | Nur Rolle `ADMIN`: Kurse, Nutzer, Landing Page | + +`/dashboard` leitet nach `/portal` um. + +--- + +## Dokumentation im Repo + +| Datei | Inhalt | +|--------|--------| +| [docs/PLAN.md](docs/PLAN.md) | Produktvision, Architektur, Phasen, Sicherheit/Monitoring | +| [docs/HANDBUCH.md](docs/HANDBUCH.md) | Betrieb: Admin, Portal, Zertifikate, Datenmodell, Docker-Details | + +--- + +## Projektstruktur (kurz) + +``` +app/ # Next.js App Router (Seiten, API-Routen) +components/ # UI-Komponenten +lib/ # Prisma-Client, Auth, Landing-Parsing, Zertifikate, Fortschritt +prisma/ # schema.prisma, Migrationen, seed.ts +scripts/ # docker-entrypoint.sh +``` + +--- + +## NPM-Skripte (ohne Docker) + +Node 20+, lokales PostgreSQL, `.env` aus `.env.example`: + +```bash +npm install +npx prisma generate +npx prisma migrate dev +npm run dev +``` + +`postinstall` führt kein `prisma generate` aus (Docker/CI-freundlich); nach `npm install` immer **`npx prisma generate`** ausführen. + +--- + +## Lizenz / Nutzung + +Internes Projekt für die Akademie – keine allgemeine Open-Source-Lizenz festgelegt, sofern nicht separat ergänzt. diff --git a/app/admin/courses/[id]/edit/page.tsx b/app/admin/courses/[id]/edit/page.tsx new file mode 100644 index 0000000..72d1b6d --- /dev/null +++ b/app/admin/courses/[id]/edit/page.tsx @@ -0,0 +1,148 @@ +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { prisma } from "@/lib/prisma"; +import { + createLessonAction, + createModuleAction, + updateCourseAction, +} from "@/app/admin/courses/actions"; + +type Props = { params: Promise<{ id: string }> }; + +export default async function AdminEditCoursePage({ params }: Props) { + const { id } = await params; + const course = await prisma.course.findUnique({ + where: { id }, + include: { + modules: { + orderBy: { sortOrder: "asc" }, + include: { lessons: { orderBy: { sortOrder: "asc" } } }, + }, + }, + }); + if (!course) notFound(); + + const priceEuros = (course.priceCents / 100).toFixed(2); + + return ( +
+

+ ← Alle Kurse ·{" "} + Öffentliche Kursseite +

+

Kurs bearbeiten

+

{course.title}

+ +
+

Stammdaten

+ + +