fix(dev): Vite-API-Proxy, Auth, Stripe-Mails und Backend-Erweiterungen

- Client: API-Basis-URL (joinApiUrl, /v1-Falle), Vite strictPort + Proxy 127.0.0.1, Nicht-JSON-Fehler

- Server: /api-404 ohne Wildcard-Bug, SPA-Fallback, Auth-Middleware, Cron, Mailer, Crypto

- Routen: OAuth-State, Email/Stripe/Analytics; client/.env.example

Made-with: Cursor
This commit is contained in:
2026-04-03 00:23:01 +02:00
parent 61008b63bb
commit ecae89a79d
33 changed files with 1663 additions and 550 deletions

View File

@@ -0,0 +1,58 @@
/**
* Appwrite JWT verification for user-scoped API routes.
*/
import { Client, Account } from 'node-appwrite'
import { config } from '../config/index.mjs'
import { AuthenticationError } from './errorHandler.mjs'
/**
* Verify Authorization: Bearer <jwt> and attach Appwrite user to req.appwriteUser
*/
export function requireAuth(req, res, next) {
;(async () => {
try {
const header = req.headers.authorization || ''
const m = /^Bearer\s+(.+)$/i.exec(header)
if (!m?.[1]) {
throw new AuthenticationError('Authorization Bearer token required')
}
const jwt = m[1].trim()
const client = new Client()
.setEndpoint(config.appwrite.endpoint)
.setProject(config.appwrite.projectId)
.setJWT(jwt)
const account = new Account(client)
const user = await account.get()
if (!user || !user.$id) {
throw new AuthenticationError('Ungültige Appwrite-Sitzung')
}
req.appwriteUser = {
id: user.$id,
email: user.email || '',
name: user.name || '',
}
next()
} catch (err) {
if (err instanceof AuthenticationError) {
next(err)
return
}
next(new AuthenticationError(err.message || 'Invalid or expired session'))
}
})()
}
/**
* Skip auth for email provider inbound webhooks only.
*/
export function requireAuthUnlessEmailWebhook(req, res, next) {
const p = req.path || ''
if (p === '/webhook/gmail' || p === '/webhook/outlook') {
return next()
}
return requireAuth(req, res, next)
}