nodebbs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -19
- package/dist/commands/pack/index.js +4 -3
- package/dist/commands/rebuild/index.js +3 -3
- package/dist/commands/start/index.d.ts +1 -1
- package/dist/commands/start/index.js +61 -70
- package/dist/templates/docker-compose.lowmem.yml +49 -3
- package/dist/templates/docker-compose.prod.yml +120 -0
- package/dist/templates/docker-compose.yml +34 -91
- package/dist/templates/env +11 -26
- package/dist/templates/init-db.sql +14 -0
- package/dist/utils/docker.d.ts +1 -1
- package/dist/utils/docker.js +5 -2
- package/dist/utils/env.d.ts +1 -1
- package/dist/utils/selection.d.ts +1 -1
- package/dist/utils/selection.js +6 -5
- package/oclif.manifest.json +112 -146
- package/package.json +1 -1
- package/dist/commands/db/import.d.ts +0 -10
- package/dist/commands/db/import.js +0 -87
|
@@ -3,37 +3,22 @@ services:
|
|
|
3
3
|
postgres:
|
|
4
4
|
image: postgres:16-alpine
|
|
5
5
|
container_name: nodebbs-postgres
|
|
6
|
-
restart:
|
|
6
|
+
restart: unless-stopped
|
|
7
7
|
environment:
|
|
8
8
|
POSTGRES_USER: postgres
|
|
9
|
-
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
9
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres_password}
|
|
10
10
|
POSTGRES_DB: ${POSTGRES_DB:-nodebbs}
|
|
11
|
-
TZ:
|
|
11
|
+
TZ: Asia/Shanghai
|
|
12
12
|
volumes:
|
|
13
13
|
- postgres_data:/var/lib/postgresql/data
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# - "${POSTGRES_PORT:-5432}:5432"
|
|
14
|
+
- ${INIT_DB_PATH:-./scripts/init-db.sql}:/docker-entrypoint-initdb.d/init.sql:ro
|
|
15
|
+
ports:
|
|
16
|
+
- "${POSTGRES_PORT:-5432}:5432"
|
|
18
17
|
healthcheck:
|
|
19
18
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
20
|
-
interval:
|
|
21
|
-
timeout:
|
|
19
|
+
interval: 10s
|
|
20
|
+
timeout: 5s
|
|
22
21
|
retries: 5
|
|
23
|
-
start_period: 40s
|
|
24
|
-
deploy:
|
|
25
|
-
resources:
|
|
26
|
-
limits:
|
|
27
|
-
cpus: '1'
|
|
28
|
-
memory: 512M
|
|
29
|
-
reservations:
|
|
30
|
-
cpus: '0.25'
|
|
31
|
-
memory: 256M
|
|
32
|
-
logging:
|
|
33
|
-
driver: "json-file"
|
|
34
|
-
options:
|
|
35
|
-
max-size: "10m"
|
|
36
|
-
max-file: "3"
|
|
37
22
|
networks:
|
|
38
23
|
- nodebbs-network
|
|
39
24
|
|
|
@@ -41,62 +26,45 @@ services:
|
|
|
41
26
|
redis:
|
|
42
27
|
image: redis:7-alpine
|
|
43
28
|
container_name: nodebbs-redis
|
|
44
|
-
restart:
|
|
45
|
-
command:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
--appendonly yes
|
|
49
|
-
--appendfsync everysec
|
|
50
|
-
--maxmemory 256mb
|
|
51
|
-
--maxmemory-policy allkeys-lru
|
|
29
|
+
restart: unless-stopped
|
|
30
|
+
command: redis-server --requirepass ${REDIS_PASSWORD:-redis_password} --appendonly yes
|
|
31
|
+
environment:
|
|
32
|
+
TZ: Asia/Shanghai
|
|
52
33
|
volumes:
|
|
53
34
|
- redis_data:/data
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# - "${REDIS_PORT:-6379}:6379"
|
|
35
|
+
ports:
|
|
36
|
+
- "${REDIS_PORT:-6379}:6379"
|
|
57
37
|
healthcheck:
|
|
58
38
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
|
59
|
-
interval:
|
|
60
|
-
timeout:
|
|
39
|
+
interval: 10s
|
|
40
|
+
timeout: 5s
|
|
61
41
|
retries: 5
|
|
62
|
-
start_period: 20s
|
|
63
|
-
deploy:
|
|
64
|
-
resources:
|
|
65
|
-
limits:
|
|
66
|
-
cpus: '0.5'
|
|
67
|
-
memory: 256M
|
|
68
|
-
reservations:
|
|
69
|
-
cpus: '0.1'
|
|
70
|
-
memory: 128M
|
|
71
|
-
logging:
|
|
72
|
-
driver: "json-file"
|
|
73
|
-
options:
|
|
74
|
-
max-size: "10m"
|
|
75
|
-
max-file: "3"
|
|
76
42
|
networks:
|
|
77
43
|
- nodebbs-network
|
|
78
44
|
|
|
79
45
|
# API 服务
|
|
80
46
|
api:
|
|
81
|
-
|
|
47
|
+
build:
|
|
48
|
+
context: . # 从 monorepo 根目录构建(turbo prune 需要完整的 workspace)
|
|
49
|
+
dockerfile: apps/api/Dockerfile
|
|
82
50
|
container_name: nodebbs-api
|
|
83
|
-
restart:
|
|
51
|
+
restart: unless-stopped
|
|
84
52
|
environment:
|
|
85
53
|
NODE_ENV: production
|
|
86
54
|
APP_NAME: ${APP_NAME:-nodebbs}
|
|
87
55
|
HOST: 0.0.0.0
|
|
88
56
|
PORT: 7100
|
|
89
|
-
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-nodebbs}
|
|
90
|
-
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379/0
|
|
91
|
-
USER_CACHE_TTL: ${USER_CACHE_TTL:-
|
|
92
|
-
JWT_SECRET: ${JWT_SECRET}
|
|
57
|
+
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-postgres_password}@postgres:5432/${POSTGRES_DB:-nodebbs}
|
|
58
|
+
REDIS_URL: redis://default:${REDIS_PASSWORD:-redis_password}@redis:6379/0
|
|
59
|
+
USER_CACHE_TTL: ${USER_CACHE_TTL:-120}
|
|
60
|
+
JWT_SECRET: ${JWT_SECRET:-change-this-to-a-secure-random-string-in-production}
|
|
93
61
|
JWT_ACCESS_TOKEN_EXPIRES_IN: ${JWT_ACCESS_TOKEN_EXPIRES_IN:-1y}
|
|
94
62
|
CORS_ORIGIN: ${CORS_ORIGIN:-*}
|
|
95
63
|
APP_URL: ${APP_URL:-http://localhost:3100}
|
|
96
|
-
TZ:
|
|
97
|
-
NODE_OPTIONS: "--max-old-space-size=512"
|
|
64
|
+
TZ: Asia/Shanghai
|
|
98
65
|
volumes:
|
|
99
66
|
- api_uploads:/app/apps/api/uploads
|
|
67
|
+
- ./apps/api/src:/app/apps/api/src:ro
|
|
100
68
|
ports:
|
|
101
69
|
- "${API_PORT:-7100}:7100"
|
|
102
70
|
depends_on:
|
|
@@ -110,35 +78,26 @@ services:
|
|
|
110
78
|
timeout: 10s
|
|
111
79
|
retries: 3
|
|
112
80
|
start_period: 40s
|
|
113
|
-
deploy:
|
|
114
|
-
resources:
|
|
115
|
-
limits:
|
|
116
|
-
cpus: '1'
|
|
117
|
-
memory: 768M
|
|
118
|
-
reservations:
|
|
119
|
-
cpus: '0.3'
|
|
120
|
-
memory: 384M
|
|
121
|
-
logging:
|
|
122
|
-
driver: "json-file"
|
|
123
|
-
options:
|
|
124
|
-
max-size: "20m"
|
|
125
|
-
max-file: "5"
|
|
126
81
|
networks:
|
|
127
82
|
- nodebbs-network
|
|
128
83
|
|
|
129
84
|
# Web 前端服务
|
|
130
85
|
web:
|
|
131
|
-
|
|
86
|
+
build:
|
|
87
|
+
context: . # 从 monorepo 根目录构建(turbo prune 需要完整的 workspace)
|
|
88
|
+
dockerfile: apps/web/Dockerfile
|
|
89
|
+
args:
|
|
90
|
+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:7100}
|
|
91
|
+
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3100}
|
|
132
92
|
container_name: nodebbs-web
|
|
133
|
-
restart:
|
|
93
|
+
restart: unless-stopped
|
|
134
94
|
environment:
|
|
135
95
|
NODE_ENV: production
|
|
136
96
|
APP_NAME: ${APP_NAME:-nodebbs}
|
|
137
97
|
PORT: 3100
|
|
138
98
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:7100}
|
|
139
99
|
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3100}
|
|
140
|
-
TZ:
|
|
141
|
-
NODE_OPTIONS: "--max-old-space-size=512"
|
|
100
|
+
TZ: Asia/Shanghai
|
|
142
101
|
ports:
|
|
143
102
|
- "${WEB_PORT:-3100}:3100"
|
|
144
103
|
depends_on:
|
|
@@ -150,19 +109,6 @@ services:
|
|
|
150
109
|
timeout: 10s
|
|
151
110
|
retries: 3
|
|
152
111
|
start_period: 40s
|
|
153
|
-
deploy:
|
|
154
|
-
resources:
|
|
155
|
-
limits:
|
|
156
|
-
cpus: '1'
|
|
157
|
-
memory: 768M
|
|
158
|
-
reservations:
|
|
159
|
-
cpus: '0.3'
|
|
160
|
-
memory: 384M
|
|
161
|
-
logging:
|
|
162
|
-
driver: "json-file"
|
|
163
|
-
options:
|
|
164
|
-
max-size: "20m"
|
|
165
|
-
max-file: "5"
|
|
166
112
|
networks:
|
|
167
113
|
- nodebbs-network
|
|
168
114
|
|
|
@@ -179,6 +125,3 @@ volumes:
|
|
|
179
125
|
networks:
|
|
180
126
|
nodebbs-network:
|
|
181
127
|
driver: bridge
|
|
182
|
-
ipam:
|
|
183
|
-
config:
|
|
184
|
-
- subnet: 172.28.0.0/16
|
package/dist/templates/env
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
# ========================================
|
|
2
|
-
# NodeBBS
|
|
2
|
+
# NodeBBS Docker Compose 环境变量配置
|
|
3
3
|
# ========================================
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
# 基础配置
|
|
7
|
-
# ========================================
|
|
8
|
-
# 应用名称(用于 Docker 容器前缀)
|
|
5
|
+
# 应用名称
|
|
9
6
|
APP_NAME=nodebbs
|
|
10
7
|
|
|
11
|
-
# 部署环境 (prod, dev)
|
|
12
|
-
NODE_ENV=production
|
|
13
|
-
|
|
14
|
-
# 镜像配置
|
|
15
|
-
# 默认为 GitHub Container Registry
|
|
16
|
-
# 注意:使用 `nodebbs start` 时,可以选择版本 tag 覆盖此处的默认 latest tag
|
|
17
|
-
# 如果需要固定特定版本 (如 v1.0),请修改以下配置或在部署时输入对应的 tag
|
|
18
|
-
API_IMAGE=ghcr.io/aiprojecthub/nodebbs-api:latest
|
|
19
|
-
WEB_IMAGE=ghcr.io/aiprojecthub/nodebbs-web:latest
|
|
20
|
-
|
|
21
8
|
# ========================================
|
|
22
9
|
# 数据库配置
|
|
23
10
|
# ========================================
|
|
24
|
-
# 务必修改为强密码!
|
|
25
11
|
POSTGRES_PASSWORD=your_secure_postgres_password_here
|
|
26
12
|
POSTGRES_DB=nodebbs
|
|
27
13
|
POSTGRES_PORT=5432
|
|
@@ -29,7 +15,6 @@ POSTGRES_PORT=5432
|
|
|
29
15
|
# ========================================
|
|
30
16
|
# Redis 配置
|
|
31
17
|
# ========================================
|
|
32
|
-
# 务必修改为强密码!
|
|
33
18
|
REDIS_PASSWORD=your_secure_redis_password_here
|
|
34
19
|
REDIS_PORT=6379
|
|
35
20
|
|
|
@@ -39,8 +24,8 @@ REDIS_PORT=6379
|
|
|
39
24
|
API_PORT=7100
|
|
40
25
|
|
|
41
26
|
# 用户缓存 TTL(秒)
|
|
42
|
-
#
|
|
43
|
-
USER_CACHE_TTL=
|
|
27
|
+
# 开发环境: 30-60, 生产环境: 120-300
|
|
28
|
+
USER_CACHE_TTL=120
|
|
44
29
|
|
|
45
30
|
# JWT 配置
|
|
46
31
|
# 使用 `openssl rand -base64 32` 生成安全的密钥
|
|
@@ -52,7 +37,6 @@ JWT_ACCESS_TOKEN_EXPIRES_IN=1y
|
|
|
52
37
|
CORS_ORIGIN=*
|
|
53
38
|
|
|
54
39
|
# 应用 URL(OAuth 回调使用)
|
|
55
|
-
# 生产环境请修改为公网域名
|
|
56
40
|
APP_URL=http://localhost:3100
|
|
57
41
|
|
|
58
42
|
# ========================================
|
|
@@ -61,16 +45,17 @@ APP_URL=http://localhost:3100
|
|
|
61
45
|
WEB_PORT=3100
|
|
62
46
|
|
|
63
47
|
# API 地址(公网访问地址)
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
|
|
48
|
+
# 使用 docker 部署必须指定 IP 或域名(避免SSR跨容器通信问题)
|
|
49
|
+
# 开发环境: http://192.168.0.100:7100
|
|
50
|
+
# 生产环境: https://api.yourdomain.com
|
|
51
|
+
NEXT_PUBLIC_API_URL=http://192.168.0.100:7100
|
|
67
52
|
|
|
68
53
|
# 应用 URL(公网访问地址)
|
|
69
|
-
#
|
|
54
|
+
# 开发环境: http://localhost:3100
|
|
55
|
+
# 生产环境: https://yourdomain.com
|
|
70
56
|
NEXT_PUBLIC_APP_URL=http://localhost:3100
|
|
71
57
|
|
|
72
58
|
# ========================================
|
|
73
|
-
#
|
|
59
|
+
# 时区配置
|
|
74
60
|
# ========================================
|
|
75
|
-
# 时区
|
|
76
61
|
TZ=Asia/Shanghai
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- PostgreSQL 初始化脚本
|
|
2
|
+
-- 这个脚本会在数据库首次创建时执行
|
|
3
|
+
|
|
4
|
+
-- 创建扩展(如果需要)
|
|
5
|
+
-- CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
6
|
+
-- CREATE EXTENSION IF NOT EXISTS "pg_trgm";
|
|
7
|
+
|
|
8
|
+
-- 设置时区
|
|
9
|
+
SET timezone = 'Asia/Shanghai';
|
|
10
|
+
|
|
11
|
+
-- 数据库已由 POSTGRES_DB 环境变量创建
|
|
12
|
+
-- 这里可以添加其他初始化 SQL
|
|
13
|
+
|
|
14
|
+
\echo '数据库初始化完成'
|
package/dist/utils/docker.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - 如果存在,则优先使用项目根目录的配置。
|
|
6
6
|
* - 如果不存在,则使用 CLI 内置的模板文件。
|
|
7
7
|
*
|
|
8
|
-
* @param env - 运行环境 ('production' | 'lowmem')
|
|
8
|
+
* @param env - 运行环境 ('production' | 'lowmem' | 'basic')
|
|
9
9
|
* @returns 对象包含文件路径数组和是否使用内置模板的标志
|
|
10
10
|
*/
|
|
11
11
|
export declare function getComposeFiles(env: string): Promise<{
|
package/dist/utils/docker.js
CHANGED
|
@@ -12,7 +12,7 @@ const fileExists = promisify(exists);
|
|
|
12
12
|
* - 如果存在,则优先使用项目根目录的配置。
|
|
13
13
|
* - 如果不存在,则使用 CLI 内置的模板文件。
|
|
14
14
|
*
|
|
15
|
-
* @param env - 运行环境 ('production' | 'lowmem')
|
|
15
|
+
* @param env - 运行环境 ('production' | 'lowmem' | 'basic')
|
|
16
16
|
* @returns 对象包含文件路径数组和是否使用内置模板的标志
|
|
17
17
|
*/
|
|
18
18
|
export async function getComposeFiles(env) {
|
|
@@ -29,7 +29,7 @@ export async function getComposeFiles(env) {
|
|
|
29
29
|
}
|
|
30
30
|
const files = [baseFile];
|
|
31
31
|
if (env === 'production') {
|
|
32
|
-
|
|
32
|
+
files.push(path.join(templateDir, 'docker-compose.prod.yml'));
|
|
33
33
|
}
|
|
34
34
|
else if (env === 'lowmem') {
|
|
35
35
|
files.push(path.join(templateDir, 'docker-compose.lowmem.yml'));
|
|
@@ -68,6 +68,9 @@ export async function runCompose(files, args, isBuiltIn = false) {
|
|
|
68
68
|
const composeArgs = files.flatMap(f => ['-f', f]);
|
|
69
69
|
if (isBuiltIn) {
|
|
70
70
|
composeArgs.push('--project-directory', process.cwd());
|
|
71
|
+
// 将 INIT_DB_PATH 设置为内置 sql 文件
|
|
72
|
+
const templateDir = path.dirname(files[0]);
|
|
73
|
+
process.env.INIT_DB_PATH = path.join(templateDir, 'init-db.sql');
|
|
71
74
|
}
|
|
72
75
|
composeArgs.push(...args);
|
|
73
76
|
// 使用 stdio: 'inherit' 实时显示输出
|
package/dist/utils/env.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function initEnv(): Promise<void>;
|
|
2
|
-
export declare function checkEnv(envType: 'production' | 'lowmem'): Promise<void>;
|
|
2
|
+
export declare function checkEnv(envType: 'production' | 'lowmem' | 'basic'): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type EnvType = 'production' | 'lowmem';
|
|
1
|
+
export type EnvType = 'production' | 'lowmem' | 'basic';
|
|
2
2
|
export declare const EnvFlag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
3
3
|
export declare function setStoredEnv(env: EnvType): Promise<void>;
|
|
4
4
|
export declare function clearStoredEnv(): Promise<void>;
|
package/dist/utils/selection.js
CHANGED
|
@@ -6,14 +6,14 @@ import path from 'node:path';
|
|
|
6
6
|
const ENV_MARKER_FILE = '.env.lock';
|
|
7
7
|
export const EnvFlag = Flags.string({
|
|
8
8
|
char: 'e',
|
|
9
|
-
description: '部署环境 (production, lowmem)',
|
|
10
|
-
options: ['production', 'lowmem'],
|
|
9
|
+
description: '部署环境 (production, lowmem, basic)',
|
|
10
|
+
options: ['production', 'lowmem', 'basic'],
|
|
11
11
|
});
|
|
12
12
|
async function getStoredEnv() {
|
|
13
13
|
try {
|
|
14
14
|
const content = await fs.readFile(path.resolve(process.cwd(), ENV_MARKER_FILE), 'utf-8');
|
|
15
15
|
const env = content.trim();
|
|
16
|
-
if (['production', 'lowmem'].includes(env)) {
|
|
16
|
+
if (['production', 'lowmem', 'basic'].includes(env)) {
|
|
17
17
|
return env;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -51,8 +51,9 @@ export async function selectEnvironment(env, options = {}) {
|
|
|
51
51
|
const selected = await select({
|
|
52
52
|
message: options.prompt || '请选择运行环境:',
|
|
53
53
|
choices: [
|
|
54
|
-
{ name: '
|
|
55
|
-
{ name: '低配环境 (1C1G)', value: 'lowmem' },
|
|
54
|
+
{ name: '标准生产环境 (2C4G+) [推荐]', value: 'production' },
|
|
55
|
+
{ name: '低配环境 (1C1G/1C2G)', value: 'lowmem' },
|
|
56
|
+
{ name: '基础环境 (仅用于测试)', value: 'basic' },
|
|
56
57
|
{ name: '❌ 取消', value: '__CANCEL__' },
|
|
57
58
|
],
|
|
58
59
|
loop: true,
|