-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
179 lines (168 loc) · 6.81 KB
/
Copy pathdocker-compose.yml
File metadata and controls
179 lines (168 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
name: "${COMPOSE_PROJECT_NAME:-note-prompt}"
x-database-connection: &database-connection
MYSQL_HOST: "${MYSQL_HOST:-docker_mysql8}"
MYSQL_PORT: "${MYSQL_PORT:-3306}"
MYSQL_DATABASE: "${MYSQL_DATABASE:-agent_report}"
services:
note-prompt-migrate:
image: "${IMAGE_REPOSITORY:-note-prompt}:${MIGRATION_IMAGE_TAG:?Set MIGRATION_IMAGE_TAG to the full Git SHA containing the migration set}"
environment:
<<: *database-connection
MYSQL_USER: "${MYSQL_MIGRATION_USER:?Set MYSQL_MIGRATION_USER to the dedicated schema migration account}"
MYSQL_PASSWORD: "${MYSQL_MIGRATION_PASSWORD:?MYSQL_MIGRATION_PASSWORD is required}"
APP_DATABASE_USER: "${MYSQL_USER:?Set MYSQL_USER to the dedicated application account}"
command:
- sh
- -ec
- |
migration_user="$$(printf '%s' "$$MYSQL_USER" | tr '[:upper:]' '[:lower:]')"
app_user="$$(printf '%s' "$$APP_DATABASE_USER" | tr '[:upper:]' '[:lower:]')"
if [ "$$migration_user" = "root" ]; then
echo "Refusing to run migrations with MYSQL_MIGRATION_USER=root" >&2
exit 1
fi
if [ "$$migration_user" = "$$app_user" ]; then
echo "Refusing to share one MySQL account between migrations and the application" >&2
exit 1
fi
exec node scripts/mysql-migrate.cjs up
init: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=32m,mode=1777
restart: "no"
networks:
- mysql-network
note-prompt-app:
image: "${IMAGE_REPOSITORY:-note-prompt}:${IMAGE_TAG:?Set IMAGE_TAG to the git commit SHA being deployed}"
build:
context: .
dockerfile: Dockerfile
args:
NODE_IMAGE: "node:24.18.0-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd"
APP_VERSION: "${IMAGE_TAG:?Set IMAGE_TAG to the git commit SHA being deployed}"
environment:
<<: *database-connection
NODE_ENV: production
PORT: "3000"
HOSTNAME: 0.0.0.0
APP_VERSION: "${IMAGE_TAG:?Set IMAGE_TAG to the git commit SHA being deployed}"
# Runtime-only database configuration. Never use the MySQL root account here.
MYSQL_USER: "${MYSQL_USER:?Set MYSQL_USER to a dedicated application account, not root}"
MYSQL_PASSWORD: "${MYSQL_PASSWORD:?MYSQL_PASSWORD is required}"
# Distributed abuse controls are mandatory in production. No in-memory fallback is allowed.
REDIS_URL: "${REDIS_URL:?REDIS_URL is required in production}"
RATE_LIMIT_ALLOW_MEMORY_FALLBACK: "false"
TRUST_PROXY: "true"
RATE_LIMIT_KEY_PREFIX: "${RATE_LIMIT_KEY_PREFIX:-note-prompt}"
AI_GLOBAL_DAILY_LIMIT: "${AI_GLOBAL_DAILY_LIMIT:-1000}"
# AI provider configuration is injected only when the container starts.
OLLAMA_HOST: "${OLLAMA_HOST:-}"
OLLAMA_PORT: "${OLLAMA_PORT:-11434}"
OLLAMA_MODEL: "${OLLAMA_MODEL:-deepseek-r1:32b}"
OLLAMA_TEMPERATURE: "${OLLAMA_TEMPERATURE:-0.1}"
OLLAMA_TIMEOUT: "${OLLAMA_TIMEOUT:-120}"
DEEPSEEK_API_KEY: "${DEEPSEEK_API_KEY:-}"
KIMI_API_KEY: "${KIMI_API_KEY:-}"
DASHSCOPE_API_KEY: "${DASHSCOPE_API_KEY:-}"
QWEN_API_KEY: "${QWEN_API_KEY:-}"
ZHIPU_API_KEY: "${ZHIPU_API_KEY:-}"
GEMINI_API_KEY: "${GEMINI_API_KEY:-}"
MINIMAX_API_KEY: "${MINIMAX_API_KEY:-}"
XIAOMI_API_KEY: "${XIAOMI_API_KEY:-}"
XIAOMI_BASE_URL: "${XIAOMI_BASE_URL:-}"
JWT_SECRET: "${JWT_SECRET:?JWT_SECRET is required}"
JWT_EXPIRES_IN: "${JWT_EXPIRES_IN:-2h}"
PROVIDER_KEY_ENCRYPTION_SECRET: "${PROVIDER_KEY_ENCRYPTION_SECRET:?PROVIDER_KEY_ENCRYPTION_SECRET is required}"
VERIFICATION_CODE_SECRET: "${VERIFICATION_CODE_SECRET:?VERIFICATION_CODE_SECRET is required}"
NEXTAUTH_URL: "${NEXTAUTH_URL:-https://noteprompt.cn}"
# Self-service entitlement changes stay opt-in; only admins are unlimited.
ENABLE_CONTRIBUTION_UPGRADES: "${ENABLE_CONTRIBUTION_UPGRADES:-false}"
PRO_AI_MONTHLY_LIMIT: "${PRO_AI_MONTHLY_LIMIT:-100}"
ENABLE_EMAIL_VERIFICATION: "${ENABLE_EMAIL_VERIFICATION:-false}"
EMAIL_HOST: "${EMAIL_HOST:-smtp.qq.com}"
EMAIL_PORT: "${EMAIL_PORT:-587}"
EMAIL_SECURE: "${EMAIL_SECURE:-false}"
EMAIL_USER: "${EMAIL_USER:-}"
EMAIL_PASS: "${EMAIL_PASS:-}"
EMAIL_FROM: "${EMAIL_FROM:-}"
EMAIL_FROM_NAME: "${EMAIL_FROM_NAME:-Note Prompt}"
expose:
- "3000"
init: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:size=256m,mode=1777
- /app/.next/cache:size=64m,mode=1777
command:
- sh
- -ec
- |
case "$$APP_VERSION" in
""|*[!0-9a-f]*)
echo "Refusing to start without a hexadecimal Git commit IMAGE_TAG" >&2
exit 1
;;
esac
if [ "$${#APP_VERSION}" -lt 7 ]; then
echo "Refusing to start with an IMAGE_TAG shorter than 7 characters" >&2
exit 1
fi
if [ "$$(printf '%s' "$$MYSQL_USER" | tr '[:upper:]' '[:lower:]')" = "root" ]; then
echo "Refusing to start with MYSQL_USER=root" >&2
exit 1
fi
exec node server.js
healthcheck:
test:
- CMD
- node
- -e
- "(async()=>{const r=await fetch('http://127.0.0.1:3000/api/health',{cache:'no-store'});const b=await r.json();if(!r.ok||b.status!=='ready'||b.checks?.database!=='up'||b.version!==process.env.APP_VERSION)throw new Error();const Redis=require('ioredis');const c=new Redis(process.env.REDIS_URL,{lazyConnect:true,maxRetriesPerRequest:1,connectTimeout:2000});try{await c.connect();if(await c.ping()!=='PONG')throw new Error()}finally{c.disconnect()}})().then(()=>process.exit(0)).catch(()=>process.exit(1))"
interval: 30s
timeout: 8s
retries: 5
start_period: 30s
stop_grace_period: 30s
restart: unless-stopped
depends_on:
note-prompt-migrate:
condition: service_completed_successfully
networks:
- note-prompt-network
- mysql-network
nginx:
image: "nginx:1.30.3-alpine@sha256:0d3b80406a13a767339fbe2f41406d6c7da727ab89cf8fae399e81f780f814d1"
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- "${TLS_CERT_DIR:-./nginx/ssl}:/etc/nginx/ssl:ro"
- "${CERTBOT_WEBROOT:-./certbot/www}:/var/www/certbot:ro"
depends_on:
note-prompt-app:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/health >/dev/null || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
networks:
- note-prompt-network
networks:
note-prompt-network:
driver: bridge
mysql-network:
name: "${MYSQL_NETWORK:-mysql8_default}"
external: true