feat: rootless Image (UID 10001), /api/health + Middleware-Ausnahme, Deploy-Compose mit Registry-Image, Gitea-CI mit Image-Push, README-Neufassung
This commit is contained in:
1 parent
92f95e5812
commit
ebb52d35c7
9 files changed
+270
-35
No files matched your search
@@ -1,6 +1,7 @@
|
||||
node_modules
|
||||
.next
|
||||
.git
|
||||
.gitea
|
||||
.worktrees
|
||||
.superpowers
|
||||
Dockerfile
|
||||
|
||||
@@ -23,3 +23,6 @@ APP_INTERNAL_URL=http://app:3000
|
||||
ALLOW_PASSWORD_LOGIN=true
|
||||
TZ=Europe/Berlin
|
||||
# Optional: OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (stattdessen UI-Einstellungen nutzen)
|
||||
# Deployment-Variante: docker-compose.deploy.yml nutzt das fertige öffentliche
|
||||
# Image gitea.lokcal.de/loki/gutscheintracker:latest – dafür ist auf dem
|
||||
# Zielserver kein Build und kein Quellcode nötig (Details siehe README).
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
# Nur der jeweils neueste Push läuft; ältere werdende Läufe werden verworfen.
|
||||
concurrency:
|
||||
group: docker-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: gitea.lokcal.de
|
||||
IMAGE: gitea.lokcal.de/loki/gutscheintracker
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
- run: npm ci --no-audit --no-fund
|
||||
- run: npm test -- --run
|
||||
- run: npx tsc --noEmit
|
||||
- run: npm run build
|
||||
|
||||
image:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: test
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Registry-Login
|
||||
env:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER || github.actor }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN || secrets.ACTION_BOT }}
|
||||
run: |
|
||||
if [ -z "$REGISTRY_TOKEN" ]; then
|
||||
echo "Registry login fehlgeschlagen: Secret REGISTRY_TOKEN oder ACTION_BOT fehlt." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
||||
- run: docker build -t "$IMAGE:latest" -t "$IMAGE:${{ github.sha }}" .
|
||||
- run: docker push "$IMAGE:latest"
|
||||
- run: docker push "$IMAGE:${{ github.sha }}"
|
||||
+17
-5
@@ -2,18 +2,30 @@ FROM node:22-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN npx prisma generate && npm run build
|
||||
|
||||
FROM node:22-alpine AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
|
||||
RUN addgroup -S app && adduser -S app -G app && mkdir -p /data && chown app:app /data
|
||||
COPY --from=builder --chown=app:app /app/.next/standalone ./
|
||||
COPY --from=builder --chown=app:app /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=app:app /app/prisma ./prisma
|
||||
USER app
|
||||
# Rootless: feste UID/GID, damit Host-Volumes und Deployments reproduzierbar
|
||||
# dieselbe Identität sehen (kein zufälliges System-UID je nach Build-Reihenfolge).
|
||||
# /data gehört der App, damit SQLite ohne Root-Rechte schreiben kann.
|
||||
RUN addgroup -S -g 10001 app && \
|
||||
adduser -S -u 10001 -G app app && \
|
||||
mkdir -p /data && chown 10001:10001 /data
|
||||
COPY --from=builder --chown=10001:10001 /app/.next/standalone ./
|
||||
COPY --from=builder --chown=10001:10001 /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=10001:10001 /app/prisma ./prisma
|
||||
# Hinweis: Caddyfile und Compose-Dateien gehören zum Deployment, nicht ins Image
|
||||
# (siehe .dockerignore) – das Image enthält nur die App.
|
||||
USER 10001:10001
|
||||
EXPOSE 3000
|
||||
# Liveness-Probe gegen den unauthentifizierten /api/health-Endpunkt.
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1:3000/api/health || exit 1
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,26 +1,124 @@
|
||||
# GutscheinNest
|
||||
|
||||
Private Gutscheinverwaltung für einen Haushalt (Next.js + SQLite + Prisma). Gemeinsamer Bestand, Anmeldung per Passwort oder SSO (OIDC), Erinnerungen per Mail/Telegram.
|
||||
Private Gutscheinverwaltung für einen oder mehrere Haushalte (Next.js + SQLite + Prisma). Gemeinsame und private Bestände, Anmeldung per Passwort oder SSO (OIDC), Erinnerungen per Mail/Telegram.
|
||||
|
||||
Rollen: Der erste Nutzer ist Admin (wird beim Start aus `APP_USER`/`APP_PASSWORD_HASH` angelegt). Admins verwalten Nutzer, Einladungen und Einstellungen; alle Mitglieder verwalten gemeinsam Gutscheine.
|
||||
Rollen: Der erste Nutzer ist Server-Admin (wird beim Start aus `APP_USER`/`APP_PASSWORD_HASH` angelegt). Server-Admins verwalten Nutzer, Einladungen, Haushalte und Einstellungen; Haushalts-Admins verwalten ihren eigenen Haushalt; alle Mitglieder verwalten gemeinsam Gutscheine.
|
||||
|
||||
## Öffentlich betreiben (Docker + Caddy + HTTPS)
|
||||
## Funktionen
|
||||
|
||||
Voraussetzungen auf dem Server: Docker mit Compose-Plugin, eine Domain mit A/AAAA-Record auf die Server-IP, offene Ports 80/443 (plus SSH für dich). Port 3000 wird **nicht** geöffnet — die App ist nur im internen Compose-Netz erreichbar.
|
||||
- Private und Haushalts-Gutscheinbestände (strikte Trennung; ein Nutzer gehört höchstens einem Haushalt)
|
||||
- Einladungen per E-Mail (7 Tage gültig), Passwort-Login und OIDC/SSO (Authentik u. a.)
|
||||
- Erinnerungen per Mail/Telegram mit frei wählbaren Vorlauf-Tagen
|
||||
- Haushalts-Export als JSON (ohne Secrets) für Server- und Haushalts-Admins
|
||||
- Kontakte, Kategorien, Suche, Erinnerungsregeln, Backup-Endpunkt für Admins
|
||||
|
||||
## Architektur
|
||||
|
||||
- `app/` – Next.js-App (App Router), API unter `app/api/`
|
||||
- `lib/` – DB-Zugriff, Auth, Zugriffsregeln (`access.ts`), Scheduler
|
||||
- `prisma/schema.prisma` – SQLite-Schema; `lib/db-migrate.ts` legt beim Boot fehlende Tabellen/Spalten an (frische und alte DBs)
|
||||
- `instrumentation.ts` → `lib/scheduler.ts` – Erinnerungs-Cron alle 15 Minuten (intern über `APP_INTERNAL_URL`, kein Hairpin über den Proxy)
|
||||
- Reverse-Proxy: Caddy (automatische Let's-Encrypt-Zertifikate, HTTP→HTTPS)
|
||||
- Container: rootless – der App-Prozess läuft als UID:GID `10001:10001`, nie als Root; Healthcheck gegen `/api/health`
|
||||
|
||||
## Schnellstart: Deployment auf einem anderen Server (fertiges Image, empfohlen)
|
||||
|
||||
Voraussetzungen auf dem Zielserver: Docker mit Compose-Plugin, eine Domain mit A/AAAA-Record auf die Server-IP, offene Ports 80/443 (plus SSH). Port 3000 wird **nicht** geöffnet – die App ist nur im internen Compose-Netz erreichbar.
|
||||
|
||||
```bash
|
||||
cp .env.example .env # Pflicht: DOMAIN (z. B. gutscheine.example.de), APP_URL=https://<DOMAIN>, APP_USER, APP_PASSWORD_HASH, SESSION_SECRET, SETTINGS_ENCRYPTION_KEY, CRON_SECRET
|
||||
# 1) Dateien holen (nur Compose + Caddy + .env-Vorlage, kein Quellcode nötig):
|
||||
# aus dem Gitea-Repo: docker-compose.deploy.yml, Caddyfile, .env.example
|
||||
cp .env.example .env # Pflicht: DOMAIN, APP_URL=https://<DOMAIN>, APP_USER,
|
||||
# APP_PASSWORD_HASH, SESSION_SECRET,
|
||||
# SETTINGS_ENCRYPTION_KEY, CRON_SECRET
|
||||
|
||||
# 2) Image ziehen und starten (öffentlich, Login nur beim ersten Pull nötig):
|
||||
docker login gitea.lokcal.de -u <gitea-benutzer>
|
||||
docker compose -f docker-compose.deploy.yml pull
|
||||
docker compose -f docker-compose.deploy.yml up -d
|
||||
```
|
||||
|
||||
Das Image `gitea.lokcal.de/loki/gutscheintracker:latest` wird bei jedem Push auf `main` per Gitea-Action frisch gebaut (nach Tests, Typcheck und Build) und ist öffentlich. Für reproduzierbare Deployments einen Digest pinnen (steht in Gitea unter → Pakete → container/gutscheintracker).
|
||||
|
||||
Prüfen:
|
||||
|
||||
- `https://<DOMAIN>/login` lädt mit gültigem Zertifikat, `http://` leitet auf HTTPS um
|
||||
- `curl http://127.0.0.1:3000/api/health` auf dem Server scheitert nicht am Proxy, sondern zeigt, dass Port 3000 nicht offen ist (kein `ports:`-Mapping)
|
||||
- Einladungs-Links und Cron-Läufe funktionieren (Test-Mail in den Einstellungen)
|
||||
- `docker exec <container> id` → `uid=10001(app) gid=10001(app)`
|
||||
|
||||
Lokal testen geht mit derselben Datei: `DOMAIN=localhost` in `.env` (dann ohne TLS, Caddy macht automatisch HTTP).
|
||||
|
||||
## Variante: lokal bauen (Entwicklung / eigener Build)
|
||||
|
||||
```bash
|
||||
cp .env.example .env # Werte ausfüllen (siehe Konfiguration)
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Caddy holt und erneuert das Let's-Encrypt-Zertifikat automatisch und leitet HTTP auf HTTPS um. `APP_URL` muss exakt die öffentliche Adresse sein (wichtig für OIDC-Redirects: in Authentik als Redirect-URI `https://<DOMAIN>/api/auth/oidc/callback` eintragen; der Reminder-Scheduler nutzt intern `APP_INTERNAL_URL`).
|
||||
App: `https://<DOMAIN>/login` (bzw. lokal `http://localhost/login` über Caddy).
|
||||
|
||||
Prüfen:
|
||||
- `https://<DOMAIN>/login` lädt mit gültigem Zertifikat, `http://` leitet auf HTTPS um.
|
||||
- Vom Server aus: Port 3000 ist geschlossen (kein `curl http://localhost:3000` möglich).
|
||||
- Einladungs-Links und Cron-Läufe funktionieren (Test-Mail in den Einstellungen).
|
||||
## Update
|
||||
|
||||
Lokal testen geht mit derselben Datei: `DOMAIN=localhost` in `.env` (dann ohne TLS, Caddy macht automatisch HTTP).
|
||||
Deployment mit Registry-Image:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.deploy.yml pull
|
||||
docker compose -f docker-compose.deploy.yml up -d
|
||||
```
|
||||
|
||||
Lokaler Build:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
## Konfiguration (`.env`)
|
||||
|
||||
| Variable | Pflicht | Beispiel | Wozu |
|
||||
|---|---|---|---|
|
||||
| `DOMAIN` | ja | `gutscheine.example.de` | Öffentliche Domain für Caddy/TLS |
|
||||
| `APP_URL` | ja | `https://gutscheine.example.de` | Exakte öffentliche Adresse (OIDC-Redirects, Einladungs-Links) |
|
||||
| `APP_INTERNAL_URL` | nein | `http://app:3000` | Interner Scheduler-Zugriff (Standard passt zu Compose) |
|
||||
| `APP_USER` / `APP_PASSWORD_HASH` | ja | `haushalt` / `$2b$10$…` | Erster Server-Admin (bcrypt-Hash, z. B. `node -e "console.log(require('bcryptjs').hashSync('…',10))"`) |
|
||||
| `ADMIN_EMAIL` | nein | `admin@beispiel.de` | Kontaktadresse |
|
||||
| `SESSION_SECRET` / `SETTINGS_ENCRYPTION_KEY` / `CRON_SECRET` | ja | je 32+ Zufallszeichen | Sessions, Einstellungs-Verschlüsselung, Cron-Schutz |
|
||||
| `SMTP_*` / `MAIL_FROM` | für Mail | – | Einladungen und Erinnerungen per Mail |
|
||||
| `TELEGRAM_BOT_TOKEN` | für Telegram | – | Erinnerungen per Telegram |
|
||||
| `ALLOW_PASSWORD_LOGIN` | nein | `true` | `false`: Mitglieder brauchen SSO (Admin behält Passwort-Notfallzugang) |
|
||||
| `TZ` | nein | `Europe/Berlin` | Zeitzone für Erinnerungen |
|
||||
| OIDC | für SSO | – | Bevorzugt in der UI (Einstellungen → SSO); `OIDC_ISSUER/CLIENT_ID/CLIENT_SECRET` auch als Env möglich |
|
||||
|
||||
In Authentik als Redirect-URI eintragen: `https://<DOMAIN>/api/auth/oidc/callback`.
|
||||
|
||||
## Mitglieder einladen
|
||||
|
||||
Admins laden unter Einstellungen → Nutzer per E-Mail ein (Link 7 Tage gültig). Eingeladene richten ein Passwort ein oder melden sich per SSO an, sofern die OIDC-E-Mail zur Einladung passt. Mit `ALLOW_PASSWORD_LOGIN=false` brauchen Mitglieder SSO; der Admin behält zusätzlich den Passwort-Notfallzugang.
|
||||
|
||||
## Backup & Wiederherstellung
|
||||
|
||||
Die SQLite-Datei liegt im Volume `gutschein-data` unter `/data/gutscheine.db`:
|
||||
|
||||
```bash
|
||||
# Sichern (Dateiname enthält den Compose-Projektnamen, ggf. anpassen):
|
||||
docker cp gutscheintracker-app-1:/data/gutscheine.db ./gutscheine-backup.db
|
||||
# oder Volume direkt kopieren:
|
||||
docker run --rm -v gutschein-data:/data -v "$PWD":/backup alpine cp /data/gutscheine.db /backup/gutscheine-backup.db
|
||||
```
|
||||
|
||||
Zusätzlich: Haushalts-Export als JSON in der UI (Einstellungen → Haushalt → „Haushaltsdaten exportieren") – enthält Gutscheine, Kontakte und Mitglieder, aber keine Passwörter oder Secrets. Der Voll-Backup-Endpunkt (`/api/settings/backup`) bleibt Server-Admins vorbehalten.
|
||||
|
||||
Wiederherstellung: Container stoppen, Datei nach `/data/gutscheine.db` zurückkopieren (Eigentümer `10001:10001` beachten), Container starten.
|
||||
|
||||
## Sicherheit
|
||||
|
||||
- Rootless: Image und Compose nutzen fest `user: "10001:10001"`; der Prozess kann keine Root-Rechte erlangen (`no-new-privileges`)
|
||||
- Kein offener App-Port: nur Caddy (80/443) ist von außen erreichbar
|
||||
- Security-Header: `nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy`, `Permissions-Policy` (ohne Kamera/Mikrofon/Standort)
|
||||
- Sessions sind signiert; Passwortwechsel invalidiert fremde Sitzungen; Admin-Resets werden protokolliert
|
||||
- Kein XSS-Risiko: keine `dangerouslySetInnerHTML`/`innerHTML`-Sinks; einziger dynamischer Link ist der validierte Shop-Link mit `rel="noopener noreferrer"`
|
||||
- Vollständige TLS-Kette via Caddy (Let's Encrypt, Auto-Renew, HTTP→HTTPS)
|
||||
|
||||
## Entwicklung
|
||||
|
||||
@@ -29,26 +127,15 @@ npm ci
|
||||
npx prisma generate
|
||||
npm run dev # http://localhost:3001
|
||||
npm test
|
||||
npx tsc --noEmit
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Mitglieder einladen
|
||||
Tests: Vitest-Suite (`tests/`), u. a. Migrationstests für Erst-Boot, Idempotenz und Altdaten-Übernahme. CI (`.gitea/workflows/docker.yml`) läuft bei jedem Push auf `main`: Tests → Typcheck → Build → Image-Build und Push (`:latest` + Commit-SHA).
|
||||
|
||||
Admins laden unter Einstellungen → Nutzer per E-Mail ein (Link 7 Tage gültig). Eingeladene richten ein Passwort ein oder melden sich per SSO an, sofern die OIDC-E-Mail zur Einladung passt. Mit `ALLOW_PASSWORD_LOGIN=false` brauchen Mitglieder SSO; der Admin behält zusätzlich den Passwort-Notfallzugang.
|
||||
## Fehlerbehebung
|
||||
|
||||
## Update
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
## Backup
|
||||
|
||||
Die SQLite-Datei liegt im Volume `gutschein-data` unter `/data/gutscheine.db`:
|
||||
|
||||
```bash
|
||||
docker cp gutscheintracker-app-1:/data/gutscheine.db ./gutscheine-backup.db
|
||||
# oder Volume direkt kopieren
|
||||
docker run --rm -v gutschein-data:/data -v "$PWD":/backup alpine cp /data/gutscheine.db /backup/gutscheine-backup.db
|
||||
```
|
||||
- Caddy holt kein Zertifikat: `DOMAIN` in `.env` prüfen, DNS A/AAAA und Ports 80/443 prüfen, `docker compose logs caddy`
|
||||
- OIDC-Login scheitert: `APP_URL` muss exakt der öffentlichen URL entsprechen; Redirect-URI in Authentik prüfen
|
||||
- Cron/Erinnerungen laufen nicht: `CRON_SECRET` gesetzt? Scheduler nutzt `APP_INTERNAL_URL` (Compose-Standard reicht)
|
||||
- DB-Schreibfehler (`unable to open database`): Volume/Datei muss der UID `10001` gehören – bei manueller Wiederherstellung `chown 10001:10001` auf die DB-Datei
|
||||
@@ -0,0 +1,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// Unauthentifizierter Liveness-Check für Docker-HEALTHCHECK und Monitoring.
|
||||
// Gibt bewusst keine internen Details preis.
|
||||
export async function GET() {
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
# Beispiel-Deployment für einen anderen Server: nutzt das fertige,
|
||||
# öffentliche Image aus der Gitea Container Registry – kein lokaler Build,
|
||||
# kein Quellcode auf dem Zielserver nötig.
|
||||
#
|
||||
# Verwendung auf dem Zielserver:
|
||||
# 1) docker login gitea.lokcal.de -u <gitea-benutzer>
|
||||
# (einmalig; danach reicht `docker compose pull` auch ohne Login,
|
||||
# da das Image öffentlich ist)
|
||||
# 2) cp .env.example .env # Pflicht: DOMAIN, APP_URL=https://<DOMAIN>,
|
||||
# # APP_USER, APP_PASSWORD_HASH, SESSION_SECRET,
|
||||
# # SETTINGS_ENCRYPTION_KEY, CRON_SECRET
|
||||
# 3) docker compose -f docker-compose.deploy.yml pull
|
||||
# 4) docker compose -f docker-compose.deploy.yml up -d
|
||||
#
|
||||
# Update auf eine neue Version:
|
||||
# docker compose -f docker-compose.deploy.yml pull
|
||||
# docker compose -f docker-compose.deploy.yml up -d
|
||||
# Pinne für reproduzierbare Deployments einen Digest statt :latest
|
||||
# (Digest steht in Gitea unter → Pakete → container/gutscheintracker).
|
||||
|
||||
services:
|
||||
app:
|
||||
image: gitea.lokcal.de/loki/gutscheintracker:latest
|
||||
# Rootless: gleiche feste UID:GID wie im Image (Dockerfile USER).
|
||||
user: "10001:10001"
|
||||
# Kein ports:-Mapping: Die App bleibt im internen Compose-Netz;
|
||||
# nach außen geht es nur über Caddy (443/HTTPS).
|
||||
expose:
|
||||
- "3000"
|
||||
environment:
|
||||
# Interner Direktzugriff für den Erinnerungs-Scheduler
|
||||
# (kein Hairpin über den öffentlichen Proxy nötig).
|
||||
- APP_INTERNAL_URL=http://app:3000
|
||||
volumes:
|
||||
- gutschein-data:/data
|
||||
env_file: [.env]
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp" # HTTP/3 (optional, schadet nicht)
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy-data:/data
|
||||
- caddy-config:/config
|
||||
environment:
|
||||
DOMAIN: ${DOMAIN:?DOMAIN muss in .env gesetzt sein (z. B. gutscheine.example.de)}
|
||||
depends_on:
|
||||
- app
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
|
||||
volumes:
|
||||
gutschein-data: {}
|
||||
caddy-data: {}
|
||||
caddy-config: {}
|
||||
@@ -1,6 +1,10 @@
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
# Rootless: Prozess läuft als UID:GID 10001:10001 (siehe Dockerfile USER).
|
||||
# Entspricht der festen Identität im Image – auch beim Deployment mit
|
||||
# fertigem Registry-Image setzen (siehe docker-compose.deploy.yml).
|
||||
user: "10001:10001"
|
||||
# Kein ports:-Mapping: Die App ist nur im internen Compose-Netz erreichbar.
|
||||
# Nach außen geht es ausschließlich über den Caddy-Reverse-Proxy (443/HTTPS).
|
||||
expose:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { verifySession, SESSION_COOKIE } from "./lib/session";
|
||||
|
||||
const PUBLIC_PREFIXES = ["/login", "/api/auth", "/accept-invite", "/api/users/accept"];
|
||||
const PUBLIC_PREFIXES = ["/login", "/api/auth", "/api/health", "/accept-invite", "/api/users/accept"];
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
const p = req.nextUrl.pathname;
|
||||
|
||||
Reference in new issue
Block a user