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:
@@ -6,14 +6,26 @@
|
||||
import express from 'express'
|
||||
import { OAuth2Client } from 'google-auth-library'
|
||||
import { ConfidentialClientApplication } from '@azure/msal-node'
|
||||
import { asyncHandler, ValidationError, AppError } from '../middleware/errorHandler.mjs'
|
||||
import { asyncHandler, ValidationError, AppError, AuthorizationError } from '../middleware/errorHandler.mjs'
|
||||
import { respond } from '../utils/response.mjs'
|
||||
import { emailAccounts } from '../services/database.mjs'
|
||||
import { config, features } from '../config/index.mjs'
|
||||
import { log } from '../middleware/logger.mjs'
|
||||
import { requireAuth } from '../middleware/auth.mjs'
|
||||
import { buildOAuthState, parseOAuthState } from '../utils/oauth-state.mjs'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
function requireAuthUnlessOAuthPublic(req, res, next) {
|
||||
const p = req.path || ''
|
||||
if (['/gmail/callback', '/outlook/callback', '/status'].includes(p)) {
|
||||
return next()
|
||||
}
|
||||
return requireAuth(req, res, next)
|
||||
}
|
||||
|
||||
router.use(requireAuthUnlessOAuthPublic)
|
||||
|
||||
// Google OAuth client (lazy initialization)
|
||||
let googleClient = null
|
||||
|
||||
@@ -71,12 +83,6 @@ const OUTLOOK_SCOPES = [
|
||||
* Initiate Gmail OAuth flow
|
||||
*/
|
||||
router.get('/gmail/connect', asyncHandler(async (req, res) => {
|
||||
const { userId } = req.query
|
||||
|
||||
if (!userId) {
|
||||
throw new ValidationError('userId ist erforderlich')
|
||||
}
|
||||
|
||||
if (!features.gmail()) {
|
||||
throw new AppError('Gmail OAuth ist nicht konfiguriert', 503, 'FEATURE_DISABLED')
|
||||
}
|
||||
@@ -86,7 +92,7 @@ router.get('/gmail/connect', asyncHandler(async (req, res) => {
|
||||
access_type: 'offline',
|
||||
scope: GMAIL_SCOPES,
|
||||
prompt: 'consent',
|
||||
state: JSON.stringify({ userId }),
|
||||
state: buildOAuthState(req.appwriteUser.id),
|
||||
include_granted_scopes: true,
|
||||
})
|
||||
|
||||
@@ -118,10 +124,10 @@ router.get('/gmail/callback', asyncHandler(async (req, res) => {
|
||||
|
||||
let userId
|
||||
try {
|
||||
const stateData = JSON.parse(state)
|
||||
const stateData = parseOAuthState(state)
|
||||
userId = stateData.userId
|
||||
} catch (e) {
|
||||
log.error('Gmail OAuth: State konnte nicht geparst werden', { state })
|
||||
log.error('Gmail OAuth: State konnte nicht geparst werden', { state, error: e.message })
|
||||
return res.redirect(`${config.frontendUrl}/settings?error=invalid_state`)
|
||||
}
|
||||
|
||||
@@ -214,6 +220,10 @@ router.post('/gmail/refresh', asyncHandler(async (req, res) => {
|
||||
|
||||
const account = await emailAccounts.get(accountId)
|
||||
|
||||
if (account.userId !== req.appwriteUser.id) {
|
||||
throw new AuthorizationError('No permission for this account')
|
||||
}
|
||||
|
||||
if (account.provider !== 'gmail') {
|
||||
throw new ValidationError('Kein Gmail-Konto')
|
||||
}
|
||||
@@ -249,12 +259,6 @@ router.post('/gmail/refresh', asyncHandler(async (req, res) => {
|
||||
* Initiate Outlook OAuth flow
|
||||
*/
|
||||
router.get('/outlook/connect', asyncHandler(async (req, res) => {
|
||||
const { userId } = req.query
|
||||
|
||||
if (!userId) {
|
||||
throw new ValidationError('userId ist erforderlich')
|
||||
}
|
||||
|
||||
if (!features.outlook()) {
|
||||
throw new AppError('Outlook OAuth ist nicht konfiguriert', 503, 'FEATURE_DISABLED')
|
||||
}
|
||||
@@ -263,7 +267,7 @@ router.get('/outlook/connect', asyncHandler(async (req, res) => {
|
||||
const authUrl = await client.getAuthCodeUrl({
|
||||
scopes: OUTLOOK_SCOPES,
|
||||
redirectUri: config.microsoft.redirectUri,
|
||||
state: JSON.stringify({ userId }),
|
||||
state: buildOAuthState(req.appwriteUser.id),
|
||||
prompt: 'select_account',
|
||||
})
|
||||
|
||||
@@ -286,7 +290,14 @@ router.get('/outlook/callback', asyncHandler(async (req, res) => {
|
||||
throw new ValidationError('Code und State sind erforderlich')
|
||||
}
|
||||
|
||||
const { userId } = JSON.parse(state)
|
||||
let userId
|
||||
try {
|
||||
userId = parseOAuthState(state).userId
|
||||
} catch (e) {
|
||||
log.error('Outlook OAuth: invalid state', { error: e.message })
|
||||
return respond.redirect(res, `${config.frontendUrl}/settings?error=invalid_state`)
|
||||
}
|
||||
|
||||
const client = getMsalClient()
|
||||
|
||||
// Exchange code for tokens
|
||||
@@ -334,6 +345,10 @@ router.post('/outlook/refresh', asyncHandler(async (req, res) => {
|
||||
|
||||
const account = await emailAccounts.get(accountId)
|
||||
|
||||
if (account.userId !== req.appwriteUser.id) {
|
||||
throw new AuthorizationError('No permission for this account')
|
||||
}
|
||||
|
||||
if (account.provider !== 'outlook') {
|
||||
throw new ValidationError('Kein Outlook-Konto')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user