Try
dfssdfsfdsf
This commit is contained in:
@@ -11,20 +11,24 @@ import { dirname, join } from 'path'
|
||||
|
||||
// Config & Middleware
|
||||
import { config, validateConfig } from './config/index.mjs'
|
||||
import { errorHandler, asyncHandler, AppError, NotFoundError, ValidationError, AuthorizationError } from './middleware/errorHandler.mjs'
|
||||
import { errorHandler, asyncHandler, AppError, ValidationError, AuthorizationError } from './middleware/errorHandler.mjs'
|
||||
import { respond } from './utils/response.mjs'
|
||||
import { logger, log } from './middleware/logger.mjs'
|
||||
import { limiters } from './middleware/rateLimit.mjs'
|
||||
import { requireAuth } from './middleware/auth.mjs'
|
||||
import { requireAuth, requireAuthUnlessEmailWebhook } from './middleware/auth.mjs'
|
||||
|
||||
// Routes
|
||||
import oauthRoutes from './routes/oauth.mjs'
|
||||
import emailRoutes from './routes/email.mjs'
|
||||
import { handleGetDigest } from './routes/email.mjs'
|
||||
import stripeRoutes from './routes/stripe.mjs'
|
||||
import { handleGetSubscriptionStatus } from './routes/stripe.mjs'
|
||||
import apiRoutes from './routes/api.mjs'
|
||||
import { handleGetReferralCode } from './routes/api.mjs'
|
||||
import analyticsRoutes from './routes/analytics.mjs'
|
||||
import webhookRoutes from './routes/webhook.mjs'
|
||||
import { startCounterJobs } from './jobs/reset-counters.mjs'
|
||||
import { startAutoSortJob } from './jobs/auto-sort.mjs'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
@@ -45,6 +49,21 @@ app.use((req, res, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
// Safety net (before all routers): collapse /api/api → /api in path until stable — old clients / bad env
|
||||
app.use((req, _res, next) => {
|
||||
const [pathPart, ...q] = req.originalUrl.split('?')
|
||||
let p = pathPart
|
||||
let prev = ''
|
||||
while (p !== prev && p.includes('/api/api')) {
|
||||
prev = p
|
||||
p = p.replace(/\/api\/api/g, '/api')
|
||||
}
|
||||
if (p !== pathPart) {
|
||||
req.url = q.length ? `${p}?${q.join('?')}` : p
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
// CORS
|
||||
app.use(cors(config.cors))
|
||||
|
||||
@@ -74,7 +93,9 @@ app.get('/api/health', (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
service: 'mailflow-api',
|
||||
status: 'healthy',
|
||||
port: config.port,
|
||||
timestamp: new Date().toISOString(),
|
||||
version: process.env.npm_package_version || '1.0.0',
|
||||
environment: config.nodeEnv,
|
||||
@@ -83,6 +104,18 @@ app.get('/api/health', (req, res) => {
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
* Route index — these three are implemented in routes/*.mjs and registered here FIRST
|
||||
* (before app.use mounts) so GET always matches real handlers, not the JSON 404 catch-all.
|
||||
*
|
||||
* GET /api/email/digest → handleGetDigest (routes/email.mjs)
|
||||
* GET /api/subscription/status → handleGetSubscriptionStatus (routes/stripe.mjs)
|
||||
* GET /api/referrals/code → handleGetReferralCode (routes/api.mjs)
|
||||
*/
|
||||
app.get('/api/email/digest', requireAuthUnlessEmailWebhook, asyncHandler(handleGetDigest))
|
||||
app.get('/api/subscription/status', requireAuth, asyncHandler(handleGetSubscriptionStatus))
|
||||
app.get('/api/referrals/code', requireAuth, asyncHandler(handleGetReferralCode))
|
||||
|
||||
// API Routes
|
||||
app.use('/api/oauth', oauthRoutes)
|
||||
app.use('/api/email', emailRoutes)
|
||||
@@ -302,20 +335,12 @@ app.delete('/api/preferences/name-labels/:id', requireAuth, asyncHandler(async (
|
||||
// Legacy Stripe webhook endpoint
|
||||
app.use('/stripe', stripeRoutes)
|
||||
|
||||
// Unmatched /api → JSON 404 (Express 4 treats '/api/*' as a literal path, not a wildcard)
|
||||
app.use((req, res, next) => {
|
||||
const pathOnly = req.originalUrl.split('?')[0]
|
||||
if (!pathOnly.startsWith('/api')) {
|
||||
return next()
|
||||
}
|
||||
next(new NotFoundError('Endpoint'))
|
||||
})
|
||||
|
||||
// SPA fallback: never send index.html for /api (avoids 404/HTML when public/index.html is missing)
|
||||
app.get('*', (req, res, next) => {
|
||||
const pathOnly = req.originalUrl.split('?')[0]
|
||||
if (pathOnly.startsWith('/api')) {
|
||||
return next(new NotFoundError('Endpoint'))
|
||||
console.warn('[404] Unmatched route:', req.method, req.originalUrl)
|
||||
return res.status(404).json({ error: 'Endpoint not found', path: req.originalUrl })
|
||||
}
|
||||
const indexPath = join(__dirname, '..', 'public', 'index.html')
|
||||
res.sendFile(indexPath, (err) => {
|
||||
@@ -331,6 +356,15 @@ app.get('*', (req, res, next) => {
|
||||
})
|
||||
})
|
||||
|
||||
// Catch-all: any method/path that did not send a response (e.g. POST /unknown)
|
||||
app.use((req, res, next) => {
|
||||
if (res.headersSent) {
|
||||
return next()
|
||||
}
|
||||
console.warn('[404] Unmatched route:', req.method, req.originalUrl)
|
||||
res.status(404).json({ error: 'Endpoint not found', path: req.originalUrl })
|
||||
})
|
||||
|
||||
// Global error handler (must be last)
|
||||
app.use(errorHandler)
|
||||
|
||||
@@ -376,6 +410,7 @@ server = app.listen(config.port, () => {
|
||||
console.log(` 💚 Health: http://localhost:${config.port}/api/health`)
|
||||
console.log('')
|
||||
startCounterJobs()
|
||||
startAutoSortJob()
|
||||
})
|
||||
|
||||
export default app
|
||||
|
||||
Reference in New Issue
Block a user