This commit is contained in:
aaron 2026-04-08 00:36:55 +08:00
parent 95047b353b
commit 02e68341f7
14 changed files with 150 additions and 64 deletions

10
backend/.dockerignore Normal file
View File

@ -0,0 +1,10 @@
__pycache__
*.pyc
*.pyo
.env
.git
.gitignore
venv
.venv
*.db
data/

19
backend/.env.production Normal file
View File

@ -0,0 +1,19 @@
# Tushare 数据源
ASTOCK_TUSHARE_TOKEN=your_tushare_token_here
# 调试模式(生产环境设为 false
ASTOCK_DEBUG=false
# DeepSeek LLM不使用 AI 对话可留空)
ASTOCK_DEEPSEEK_API_KEY=
# JWT 安全配置(生产环境务必修改!)
ASTOCK_JWT_SECRET=替换为一个随机长字符串
ASTOCK_JWT_EXPIRY_HOURS=24
# 默认管理员账号(首次启动自动创建)
ASTOCK_ADMIN_USERNAME=admin
ASTOCK_ADMIN_PASSWORD=替换为一个安全密码
# 前端地址(用于 CORS
ASTOCK_FRONTEND_URL=http://your-server-ip:3000

15
backend/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 数据库文件存放在 /app/data 目录,通过 volume 持久化
RUN mkdir -p /app/data
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@ -1,26 +1,29 @@
version: "3.8"
services: services:
backend: backend:
build: ./backend build: ./backend
ports: restart: unless-stopped
- "8000:8000"
env_file: env_file:
- ./backend/.env - ./backend/.env
environment:
- ASTOCK_DATABASE_URL=sqlite:///./data/astock.db
volumes: volumes:
- ./backend:/app
- db_data:/app/data - db_data:/app/data
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload expose:
- "8000"
# 生产环境不需要映射端口,前端容器通过 Docker 网络内部访问
frontend: frontend:
build: ./frontend build:
context: ./frontend
args:
- BACKEND_URL=http://backend:8000
restart: unless-stopped
ports: ports:
- "3000:3000" - "3000:3000"
depends_on: depends_on:
- backend - backend
environment: environment:
- NEXT_PUBLIC_API_URL=http://backend:8000 - BACKEND_URL=http://backend:8000
command: npm run dev
volumes: volumes:
db_data: db_data:

4
frontend/.dockerignore Normal file
View File

@ -0,0 +1,4 @@
node_modules
.next
.git
.env*

View File

@ -16,13 +16,6 @@
"static/chunks/448-92a7b932cf4502ac.js", "static/chunks/448-92a7b932cf4502ac.js",
"static/chunks/app/layout-97bdd231e78ddf3f.js" "static/chunks/app/layout-97bdd231e78ddf3f.js"
], ],
"/login/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js",
"static/chunks/fd9d1056-f8a2d551cbb94c85.js",
"static/chunks/117-d0aa9486d6cf1a7a.js",
"static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/login/page-778febf452923618.js"
],
"/page": [ "/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",
"static/chunks/fd9d1056-f8a2d551cbb94c85.js", "static/chunks/fd9d1056-f8a2d551cbb94c85.js",
@ -30,12 +23,19 @@
"static/chunks/main-app-7d7e5d1021afd90c.js", "static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/page-5a303311159f23ad.js" "static/chunks/app/page-5a303311159f23ad.js"
], ],
"/recommendations/page": [ "/login/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",
"static/chunks/fd9d1056-f8a2d551cbb94c85.js", "static/chunks/fd9d1056-f8a2d551cbb94c85.js",
"static/chunks/117-d0aa9486d6cf1a7a.js", "static/chunks/117-d0aa9486d6cf1a7a.js",
"static/chunks/main-app-7d7e5d1021afd90c.js", "static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/recommendations/page-ef6715bbb27168f0.js" "static/chunks/app/login/page-778febf452923618.js"
],
"/chat/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js",
"static/chunks/fd9d1056-f8a2d551cbb94c85.js",
"static/chunks/117-d0aa9486d6cf1a7a.js",
"static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/chat/page-2dd3304322bd4036.js"
], ],
"/sectors/page": [ "/sectors/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",
@ -44,12 +44,12 @@
"static/chunks/main-app-7d7e5d1021afd90c.js", "static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/sectors/page-2f0e16a2b83354cf.js" "static/chunks/app/sectors/page-2f0e16a2b83354cf.js"
], ],
"/chat/page": [ "/recommendations/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",
"static/chunks/fd9d1056-f8a2d551cbb94c85.js", "static/chunks/fd9d1056-f8a2d551cbb94c85.js",
"static/chunks/117-d0aa9486d6cf1a7a.js", "static/chunks/117-d0aa9486d6cf1a7a.js",
"static/chunks/main-app-7d7e5d1021afd90c.js", "static/chunks/main-app-7d7e5d1021afd90c.js",
"static/chunks/app/chat/page-2dd3304322bd4036.js" "static/chunks/app/recommendations/page-ef6715bbb27168f0.js"
], ],
"/stock/[code]/page": [ "/stock/[code]/page": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",

View File

@ -5,8 +5,8 @@
"devFiles": [], "devFiles": [],
"ampDevFiles": [], "ampDevFiles": [],
"lowPriorityFiles": [ "lowPriorityFiles": [
"static/77YFCNa-r_QnYgqwH2vOe/_buildManifest.js", "static/zyMEF2O04rWB2PcTlD48N/_buildManifest.js",
"static/77YFCNa-r_QnYgqwH2vOe/_ssgManifest.js" "static/zyMEF2O04rWB2PcTlD48N/_ssgManifest.js"
], ],
"rootMainFiles": [ "rootMainFiles": [
"static/chunks/webpack-76aa9cbbdedb6a49.js", "static/chunks/webpack-76aa9cbbdedb6a49.js",

View File

@ -1,11 +1,11 @@
{ {
"/_not-found/page": "app/_not-found/page.js", "/_not-found/page": "app/_not-found/page.js",
"/login/page": "app/login/page.js",
"/api/chat/stream/route": "app/api/chat/stream/route.js", "/api/chat/stream/route": "app/api/chat/stream/route.js",
"/page": "app/page.js", "/page": "app/page.js",
"/recommendations/page": "app/recommendations/page.js", "/login/page": "app/login/page.js",
"/sectors/page": "app/sectors/page.js",
"/chat/page": "app/chat/page.js", "/chat/page": "app/chat/page.js",
"/sectors/page": "app/sectors/page.js",
"/recommendations/page": "app/recommendations/page.js",
"/stock/[code]/page": "app/stock/[code]/page.js", "/stock/[code]/page": "app/stock/[code]/page.js",
"/users/page": "app/users/page.js" "/users/page": "app/users/page.js"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"node":{},"edge":{},"encryptionKey":"mEKzCdLXC9Tw5xpwGdIONxBrCnit3XJOsa5Cm7fALlw="} {"node":{},"edge":{},"encryptionKey":"f3swqWaUk34ppPaPfJEtsu7hIqrZ76kvi8fuvlEN4Xc="}

File diff suppressed because one or more lines are too long

32
frontend/Dockerfile Normal file
View File

@ -0,0 +1,32 @@
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
COPY . .
# 构建时注入后端地址(运行时通过 rewrites 代理,这里设为内部地址)
ARG BACKEND_URL=http://backend:8000
ENV BACKEND_URL=${BACKEND_URL}
RUN npm run build
# ---- Production image ----
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View File

@ -1,14 +1,17 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const backendUrl = process.env.BACKEND_URL || "http://localhost:8000";
const nextConfig = { const nextConfig = {
output: "standalone",
async rewrites() { async rewrites() {
return [ return [
{ {
source: "/api/:path*", source: "/api/:path*",
destination: "http://localhost:8000/api/:path*", destination: `${backendUrl}/api/:path*`,
}, },
{ {
source: "/ws", source: "/ws",
destination: "http://localhost:8000/ws", destination: `${backendUrl}/ws`,
}, },
]; ];
}, },