mirror of
https://github.com/halo-dev/upage.git
synced 2026-05-07 05:38:23 +08:00
* chore: upgrade from remix to react router v7 * chore: upgrade from remix to react router v7 * Refactor API routes to flat structure and simplify handlers Migrated API route files from dynamic Remix v2 file system routing to a flat, explicit structure compatible with React Router v7. Removed all dynamic route handler files and replaced them with direct route modules under organized folders (e.g., api/chat, api/1panel, etc.). Updated route configuration in routes.ts to use the new structure and prefix helpers. Refactored all API handler modules to include authentication checks directly and removed indirection via action/loader wrappers. Updated imports and fixed references throughout the codebase to match the new file locations. Also updated Netlify deploy action endpoint and improved error handling in 1panel store. * Refactor route handlers and update component props Refactored several route handler functions to remove unnecessary exports and align with new conventions. Updated Chat component to receive loaderData via props and adjusted usage in chat route. Removed unused 'data' utility from routes. Minor UI component cleanup and fixed transition utility in uno.config.ts. * adjust server abort delay * Remove AuthErrorToast and update react-router packages Deleted the AuthErrorToast component and its lazy loader from the codebase. Upgraded all @react-router/* and react-router dependencies from version 7.10.1 to 7.11.0 in package.json and pnpm-lock.yaml. * Refactor route loader data typing and route IDs Replaces manual type definitions for route loader data with types from generated Route types, improving type safety and maintainability. Updates useRouteLoaderData calls and adjusts route registration to use explicit route IDs, aligning with new type usage. * Clean up Remix naming remnants after React Router v7 migration * feat: add claude skills
103 lines
2.9 KiB
JavaScript
103 lines
2.9 KiB
JavaScript
import { createRequestHandler } from '@react-router/express';
|
|
import compression from 'compression';
|
|
import cors from 'cors';
|
|
import express from 'express';
|
|
import rateLimit from 'express-rate-limit';
|
|
import morgan from 'morgan';
|
|
import path from 'path';
|
|
|
|
const viteDevServer =
|
|
process.env.NODE_ENV === 'production'
|
|
? undefined
|
|
: await import('vite').then((vite) =>
|
|
vite.createServer({
|
|
server: { middlewareMode: true },
|
|
}),
|
|
);
|
|
|
|
const reactRouterHandler = createRequestHandler({
|
|
build: viteDevServer
|
|
? () => viteDevServer.ssrLoadModule('virtual:react-router/server-build')
|
|
: await import('./build/server/index.js'),
|
|
});
|
|
|
|
const app = express();
|
|
|
|
app.set('trust proxy', true);
|
|
|
|
app.use(
|
|
cors({
|
|
// 允许所有来源访问,生产环境中应该设置为特定的域名
|
|
origin: '*',
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
|
credentials: true,
|
|
maxAge: 86400,
|
|
}),
|
|
);
|
|
|
|
// 配置全局限流中间件
|
|
const globalLimiter = rateLimit({
|
|
windowMs: 60 * 1000, // 1 分钟
|
|
limit: 1000, // 每个 IP 每分钟最多 1000 个请求
|
|
standardHeaders: 'draft-7', // 返回标准的 RateLimit 头信息
|
|
legacyHeaders: false, // 禁用旧的 X-RateLimit 头信息
|
|
message: '请求过于频繁,请稍后再试',
|
|
proxy: true,
|
|
});
|
|
|
|
// 针对聊天 API 的特殊限流中间件
|
|
const chatApiLimiter = rateLimit({
|
|
windowMs: 60 * 1000,
|
|
limit: 5, // 每个 IP 每分钟最多 5 个聊天请求
|
|
standardHeaders: 'draft-7',
|
|
legacyHeaders: false,
|
|
message: '聊天请求过于频繁,请稍后再试',
|
|
// 仅对聊天 API 路由应用此限制
|
|
skip: (req) => !req.url.includes('/api/chat'),
|
|
proxy: true,
|
|
});
|
|
|
|
app.use((req, res, next) => {
|
|
if (req.url.startsWith('/assets') || req.url.startsWith('/build') || req.url.includes('.')) {
|
|
return next();
|
|
}
|
|
|
|
next();
|
|
});
|
|
|
|
app.use(compression());
|
|
|
|
app.use(globalLimiter);
|
|
|
|
app.use('/api/chat', chatApiLimiter);
|
|
|
|
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
|
|
app.disable('x-powered-by');
|
|
|
|
// handle asset requests
|
|
if (viteDevServer) {
|
|
app.use(viteDevServer.middlewares);
|
|
} else {
|
|
// Vite fingerprints its assets so we can cache forever.
|
|
app.use('/assets', express.static('build/client/assets', { immutable: true, maxAge: '1y' }));
|
|
}
|
|
|
|
// Everything else (like favicon.ico) is cached for an hour. You may want to be
|
|
// more aggressive with this caching.
|
|
app.use(express.static('build/client', { maxAge: '1h' }));
|
|
|
|
// 添加对上传文件的静态服务支持
|
|
const storageDir = process.env.STORAGE_DIR || path.join(process.cwd(), 'public', 'uploads');
|
|
app.use('/uploads', express.static(storageDir, { maxAge: '1y' }));
|
|
|
|
app.use(morgan('tiny'));
|
|
|
|
// handle SSR requests
|
|
app.use(reactRouterHandler);
|
|
|
|
const port = process.env.PORT || 3000;
|
|
app.listen(port, () => {
|
|
console.log(`Express server listening at http://localhost:${port}`);
|
|
});
|