Merge origin/main in feature/terminplaner-tp2 (Analyse-Cockpit, Subdomain-Sperre, Projekte)

Konflikte: funktionen.js folgt der Aufteilung aus main (fn-util.js, analyse.js); der Link
'Zu Verträge' der Analyse-Seite kommt jetzt auch in analyse.js aus vertraege-link.js.
Checkout und Team-Ansicht übernehmen assertOwnProject(..., { forNewOrder: true }) und
includeArchived aus main. Test-Pins der absoluten Importe nachgezogen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 07:11:58 +00:00
commit af63b69ec1
56 files changed
+4583 -717

No files matched your search

+18 -4
View File
@@ -1,6 +1,20 @@
import { config } from '../config.js'
import { ADMIN_LABEL, STAFF_LABELS } from '../services/staffLabels.js'
export async function requireAppwriteStaff(req, res, next) {
/**
* Prueft das Appwrite-JWT aus dem Ticketsystem und verlangt eines der Labels `allowed`.
* requireAppwriteStaff: nur "admin" (Kunden, Mitarbeiter, Finanzen ...).
* requireAppwriteTeam: jeder Mitarbeiter ("mitarbeiter" oder "admin") - Projekt-Aktionen
* (Anlegen, Zuordnen, Archivieren, Preview, Analyse, Gitea-Import), Entscheidung 2026-10-04.
*/
function requireAppwriteLabels(allowed, deniedMessage) {
return (req, res, next) => checkAppwriteJwt(req, res, next, allowed, deniedMessage)
}
export const requireAppwriteStaff = requireAppwriteLabels([ADMIN_LABEL], 'Admin-Berechtigung erforderlich')
export const requireAppwriteTeam = requireAppwriteLabels(STAFF_LABELS, 'Nur für Mitarbeiter')
async function checkAppwriteJwt(req, res, next, allowed, deniedMessage) {
const authHeader = req.get('Authorization') || ''
const match = authHeader.match(/^Bearer\s+(.+)$/i)
if (!match) {
@@ -29,11 +43,11 @@ export async function requireAppwriteStaff(req, res, next) {
}
if (!response.ok) {
return res.status(401).json({ error: data?.message || 'Ung�ltiges JWT' })
return res.status(401).json({ error: data?.message || 'Ungültiges JWT' })
}
if (!Array.isArray(data?.labels) || !data.labels.includes('admin')) {
return res.status(403).json({ error: 'Admin-Berechtigung erforderlich' })
if (!Array.isArray(data?.labels) || !allowed.some((label) => data.labels.includes(label))) {
return res.status(403).json({ error: deniedMessage })
}
req.appwriteUser = data
+1 -1
View File
@@ -7,7 +7,7 @@ export function corsMiddleware(allowedOrigins) {
if (origin && allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Access-Control-Allow-Headers', ALLOW_HEADERS)
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
if (req.method === 'OPTIONS') {
return res.sendStatus(204)
}