/** * API 路由主入口(客户独立实例) * - Amazon:保留本地 SP-API 模块 * - Sorftime / TikHub:浏览器直打公司中转;定时任务经 src/relay RelayClient * - 已移除:直连 sorftime / tikhub / eccang 模块 */ import express from 'npm:express'; const router = express.Router(); console.log('[API Routes] Initializing Jianhen customer instance...'); router.use((req, res, next) => { console.log(`[API Router] Hit: ${req.method} ${req.url} (Original: ${req.originalUrl})`); next(); }); router.get('/ping', (req, res) => { res.json({ message: 'pong', instance: 'jianhen-voc' }); }); function isInternalRequest(req) { const address = String(req.socket?.remoteAddress || ''); return !req.headers['x-external-request'] && (address === '127.0.0.1' || address === '::1' || address.endsWith(':127.0.0.1')); } async function requireApiUser(req, res, next) { if (isInternalRequest(req)) return next(); const token = String(req.headers['x-parse-session-token'] || '').trim(); if (!token) return res.status(401).json({ success: false, message: '需要登录' }); try { const Parse = globalThis.Parse; const query = new Parse.Query('_Session'); query.equalTo('sessionToken', token); query.include('user'); const session = await query.first({ useMasterKey: true }); const user = session?.get('user'); if (!user || user.get('status') === 'disabled') { return res.status(401).json({ success: false, message: '会话无效' }); } req.currentUser = user; next(); } catch { res.status(401).json({ success: false, message: '会话无效' }); } } function requireAdmin(req, res, next) { if (isInternalRequest(req) || req.currentUser?.get?.('isAdmin') === true) return next(); res.status(403).json({ success: false, message: '需要管理员权限' }); } function requireUserExceptHealth(req, res, next) { if (req.path === '/health') return next(); return requireApiUser(req, res, next); } function requireAdminExceptToken(req, res, next) { if (req.path === '/token') return next(); return requireAdmin(req, res, next); } /** * 亚马逊相关接口 * 必须携带店铺的 id */ import { createSpApiRouter } from '../modules/fmode-amazon-sp-api/src/mod.ts'; router.use('/amazon', requireApiUser, createSpApiRouter()); /** * 公司中转密钥/余额:仅管理员 * Sorftime / TikHub 浏览器请求不再经过本机 /forward。 */ import relayCredentialRouter from './relay/credential-routes.ts'; router.use('/relay', requireApiUser, requireAdminExceptToken, relayCredentialRouter); console.log('[API Routes] ✅ Company relay credential routes mounted'); // 定时任务管理路由(内部已改为 RelayClient 直打公司中转) import scheduleRouter from './module/schedule/routes-schedule.ts'; try { router.use('/schedule', requireApiUser, requireAdmin, scheduleRouter); } catch (e) { console.warn('[API Routes] scheduleRouter 加载失败', e); } router.get('/', (req, res) => { res.json({ message: 'Jianhen VOC Instance API Routes Loaded', version: '1.0.1-customer-relay', timestamp: new Date().toISOString(), availableRoutes: [ '/api/amazon', '/api/relay/token', '/api/relay/status', '/api/relay/credential', '/api/schedule', ], removedModules: [ 'fmode-sorftime-api (direct upstream)', 'fmode-tikhub-api (direct upstream)', 'fmode-tikhub-api-customize', 'fmode-eccang-api', ], relay: { companyBase: 'https://server.fmode.cn', sorftimeUpstream: '/api/voc-ecom/forward', tikhubUpstream: '/api/voc-social/*', }, }); }); export default router;