From bd99fc8e814b5f15ec501d7f747fc81d4830e51d Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 18 Mar 2026 16:13:01 +0800 Subject: [PATCH] feat: add new server entry point and update package.json --- .env.example | 15 +++++++++ package.json | 27 +++++++++++++--- server/index.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 .env.example create mode 100644 server/index.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e1e02b8 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# 服务器配置 +PORT=3501 +NODE_ENV=development + +# JWT 密钥(生产环境必须修改) +JWT_SECRET=your-secret-key-here + +# 数据库路径 +DB_PATH=./accountbook.db + +# 日志级别 +LOG_LEVEL=info + +# 生产环境允许的域名(逗号分隔) +ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com diff --git a/package.json b/package.json index c679971..2a69a4d 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,35 @@ { "name": "accountbook", - "version": "1.0.0", + "version": "1.1.0", "description": "简易记账本", - "main": "server.js", + "main": "server/index.js", "scripts": { - "start": "node server.js" + "start": "node server/index.js", + "dev": "concurrently \"npm run server:dev\" \"npm run client:dev\"", + "server:dev": "node server/index.js", + "client:dev": "cd frontend && npm run dev", + "build": "cd frontend && npm run build", + "migrate": "node server/db/migrate.js", + "test": "jest", + "lint": "eslint server/ frontend/", + "lint:fix": "eslint server/ frontend/ --fix" }, "dependencies": { "express": "^4.18.2", "better-sqlite3": "^9.2.2", "jsonwebtoken": "^9.0.2", "bcryptjs": "^2.4.3", - "cors": "^2.8.5" + "cors": "^2.8.5", + "dotenv": "^16.3.1" + }, + "devDependencies": { + "concurrently": "^8.2.2", + "eslint": "^8.55.0", + "jest": "^29.7.0", + "nodemon": "^3.0.2", + "prettier": "^3.1.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-node": "^11.1.0", + "supertest": "^6.3.3" } } diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..a2d8ca0 --- /dev/null +++ b/server/index.js @@ -0,0 +1,83 @@ +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const fs = require('fs'); + +// 确保日志目录存在 +const logsDir = path.join(__dirname, '../logs'); +if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); +} + +// 加载环境变量 +if (fs.existsSync(path.join(__dirname, '../.env'))) { + require('dotenv').config({ path: path.join(__dirname, '../.env') }); +} + +const { errorHandler } = require('./middleware/errorHandler'); + +// 运行数据库迁移 +require('./db/migrate'); + +const app = express(); +const PORT = process.env.PORT || 3501; + +// 中间件 +app.use(cors({ + origin: process.env.NODE_ENV === 'production' + ? (process.env.ALLOWED_ORIGINS || '').split(',').filter(Boolean) + : ['http://localhost:3500', 'http://127.0.0.1:3500'], + credentials: true, +})); + +app.use(express.json()); +app.use(express.static(path.join(__dirname, '../public'))); +app.use(express.static(path.join(__dirname, '../frontend/.next'))); +app.use(express.static(path.join(__dirname, '../frontend/.next/server/pages'))); + +// 请求日志 +app.use((req, res, next) => { + console.log(`${req.method} ${req.url}`); + next(); +}); + +// 路由 +app.use('/api/auth', require('./routes/auth')); +app.use('/api/records', require('./routes/records')); +app.use('/api/stats', require('./routes/stats')); +app.use('/api/recurring', require('./routes/recurring')); +app.use('/api/categories', require('./routes/categories')); +app.use('/api/export', require('./routes/export')); +app.use('/api/search', require('./routes/search')); + +// 健康检查 +app.get('/api/health', (req, res) => { + res.json({ status: 'ok', timestamp: new Date().toISOString() }); +}); + +// 前端路由 - Next.js 静态文件 +app.get('/_next/static/:path(*)', (req, res) => { + const filePath = path.join(__dirname, '../frontend/.next/static', req.params.path); + res.sendFile(filePath, (err) => { + if (err) res.status(404).send('Not found'); + }); +}); + +app.get('*', (req, res) => { + let pagePath = req.path === '/' ? '/index.html' : req.path + '.html'; + const nextPagePath = path.join(__dirname, '../frontend/.next/server/pages', pagePath); + + res.sendFile(nextPagePath, (err) => { + if (err) { + res.sendFile(path.join(__dirname, '../frontend/.next/server/pages/index.html')); + } + }); +}); + +// 错误处理 +app.use(errorHandler); + +// 启动服务器 +app.listen(PORT, '0.0.0.0', () => { + console.log(`记账本服务已启动: http://0.0.0.0:${PORT}`); +});