@optima-chat/dev-skills 0.7.19 → 0.7.21
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/.claude/commands/logs.md +15 -0
- package/.claude/commands/query-db.md +30 -0
- package/.claude/commands/restart-ecs.md +12 -0
- package/.claude/settings.local.json +46 -0
- package/.claude/skills/logs/SKILL.md +1 -0
- package/.claude/skills/query-db/SKILL.md +2 -0
- package/.claude/skills/restart-ecs/SKILL.md +2 -0
- package/.claude/skills/show-env/SKILL.md +1 -0
- package/bin/cli.js +2 -1
- package/bin/helpers/query-db.ts +71 -13
- package/bin/helpers/show-env.ts +6 -1
- package/dist/bin/helpers/generate-test-token.js +0 -0
- package/dist/bin/helpers/query-db.js +66 -11
- package/dist/bin/helpers/show-env.js +6 -1
- package/package.json +1 -1
package/.claude/commands/logs.md
CHANGED
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
- `optima-store` - 商城前端(仅 Stage)
|
|
32
32
|
- `commerce-rq-worker` - RQ 后台任务
|
|
33
33
|
- `commerce-rq-scheduler` - RQ 定时调度
|
|
34
|
+
- `optima-logistics` - 物流服务(仅 Stage/Prod)
|
|
35
|
+
- `billing` - 计费服务(仅 Stage/Prod)
|
|
36
|
+
- `browser-backend` - 浏览器自动化服务(仅 Stage/Prod)
|
|
37
|
+
- `optima-generation` - 内容生成服务(仅 Stage/Prod)
|
|
38
|
+
- `optima-generation-worker` - 内容生成 Worker(仅 Stage/Prod)
|
|
34
39
|
- `lines` (可选): 显示行数,默认 50
|
|
35
40
|
- `environment` (可选): 环境,默认 ci
|
|
36
41
|
- `ci` - CI 持续集成环境(开发环境,默认)
|
|
@@ -126,6 +131,11 @@ aws logs get-log-events --log-group-name /ecs/commerce-backend-stage --log-strea
|
|
|
126
131
|
- `optima-store` → `/ecs/optima-store-stage`
|
|
127
132
|
- `commerce-rq-worker` → `/ecs/commerce-rq-worker-stage`
|
|
128
133
|
- `commerce-rq-scheduler` → `/ecs/commerce-rq-scheduler-stage`
|
|
134
|
+
- `optima-logistics` → `/ecs/optima-logistics-stage`
|
|
135
|
+
- `billing` → `/ecs/billing-stage`
|
|
136
|
+
- `browser-backend` → `/ecs/browser-backend-stage`
|
|
137
|
+
- `optima-generation` → `/ecs/optima-generation-stage`
|
|
138
|
+
- `optima-generation-worker` → `/ecs/optima-generation-worker-stage`
|
|
129
139
|
|
|
130
140
|
### 2. Prod 环境(environment = "prod")
|
|
131
141
|
|
|
@@ -164,6 +174,11 @@ aws logs get-log-events --log-group-name /ecs/commerce-backend-prod --log-stream
|
|
|
164
174
|
- `optima-scout` → `/ecs/optima-scout-prod`
|
|
165
175
|
- `commerce-rq-worker` → `/ecs/commerce-rq-worker-prod`
|
|
166
176
|
- `commerce-rq-scheduler` → `/ecs/commerce-rq-scheduler-prod`
|
|
177
|
+
- `optima-logistics` → `/ecs/optima-logistics-prod`
|
|
178
|
+
- `billing` → `/ecs/billing-prod`
|
|
179
|
+
- `browser-backend` → `/ecs/browser-backend-prod`
|
|
180
|
+
- `optima-generation` → `/ecs/optima-generation-prod`
|
|
181
|
+
- `optima-generation-worker` → `/ecs/optima-generation-worker-prod`
|
|
167
182
|
|
|
168
183
|
**注意**: `optima-store` 仅在 Stage 环境部署
|
|
169
184
|
|
|
@@ -47,6 +47,10 @@ optima-query-db commerce-backend "SELECT * FROM products LIMIT 5" prod
|
|
|
47
47
|
- `agentic-chat` - AI 聊天服务数据库
|
|
48
48
|
- `bi-backend` - BI 后端数据库
|
|
49
49
|
- `session-gateway` - AI Shell 网关数据库
|
|
50
|
+
- `optima-logistics` - 物流服务数据库
|
|
51
|
+
- `billing` - 计费服务数据库
|
|
52
|
+
- `browser-backend` - 浏览器自动化服务数据库
|
|
53
|
+
- `optima-generation` - 内容生成服务数据库
|
|
50
54
|
- `sql` (必需): SQL 查询语句(用引号包裹)
|
|
51
55
|
- `environment` (可选): 环境,默认 ci
|
|
52
56
|
- `ci` - CI 持续集成环境(开发环境,默认)
|
|
@@ -245,9 +249,22 @@ pkill -f "ssh.*15432:${DATABASE_HOST}:5432"
|
|
|
245
249
|
- 用户: Infisical `AI_SHELL_DB_USER`
|
|
246
250
|
- 密码: Infisical `AI_SHELL_DB_PASSWORD`
|
|
247
251
|
|
|
252
|
+
- `billing`:
|
|
253
|
+
- 数据库: `optima_billing_stage`
|
|
254
|
+
- 凭证: Infisical `/services/billing` → `DATABASE_URL`
|
|
255
|
+
|
|
256
|
+
- `browser-backend`:
|
|
257
|
+
- 数据库: `optima_stage_browser`
|
|
258
|
+
- 凭证: Infisical `/services/browser-backend` → `DATABASE_URL`
|
|
259
|
+
|
|
260
|
+
- `optima-generation`:
|
|
261
|
+
- 数据库: `optima_generation_stage`
|
|
262
|
+
- 凭证: Infisical `/services/optima-generation` → `DATABASE_URL`
|
|
263
|
+
|
|
248
264
|
**说明**:
|
|
249
265
|
- Infisical 配置从 GitHub Variables 获取
|
|
250
266
|
- 数据库密钥从 Infisical 动态获取(项目: optima-secrets-v2, 环境: staging, 路径: /shared-secrets/database-users)
|
|
267
|
+
- billing、browser-backend、optima-generation 的凭证存在各自服务路径的 DATABASE_URL 中
|
|
251
268
|
- Stage RDS: `optima-stage-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com`
|
|
252
269
|
- Shared EC2 IP: `3.0.210.113`
|
|
253
270
|
- SSH 隧道: 本地端口 `15432` → Shared EC2 → Stage RDS `5432`
|
|
@@ -343,9 +360,22 @@ pkill -f "ssh.*15433:${DATABASE_HOST}:5432"
|
|
|
343
360
|
- 用户: Infisical `AI_SHELL_DB_USER`
|
|
344
361
|
- 密码: Infisical `AI_SHELL_DB_PASSWORD`
|
|
345
362
|
|
|
363
|
+
- `billing`:
|
|
364
|
+
- 数据库: `optima_billing`
|
|
365
|
+
- 凭证: Infisical `/services/billing` → `DATABASE_URL`
|
|
366
|
+
|
|
367
|
+
- `browser-backend`:
|
|
368
|
+
- 数据库: `optima_browser`
|
|
369
|
+
- 凭证: Infisical `/services/browser-backend` → `DATABASE_URL`
|
|
370
|
+
|
|
371
|
+
- `optima-generation`:
|
|
372
|
+
- 数据库: `optima_generation`
|
|
373
|
+
- 凭证: Infisical `/services/optima-generation` → `DATABASE_URL`
|
|
374
|
+
|
|
346
375
|
**说明**:
|
|
347
376
|
- Infisical 配置从 GitHub Variables 获取
|
|
348
377
|
- 数据库密钥从 Infisical 动态获取(项目: optima-secrets-v2, 环境: prod, 路径: /shared-secrets/database-users)
|
|
378
|
+
- billing、browser-backend、optima-generation 的凭证存在各自服务路径的 DATABASE_URL 中
|
|
349
379
|
- Prod RDS: `optima-prod-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com`
|
|
350
380
|
- Shared EC2 IP: `3.0.210.113`
|
|
351
381
|
- SSH 隧道: 本地端口 `15433` → Shared EC2 → Prod RDS `5432`
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
- `optima-scout` - 产品研究工具
|
|
32
32
|
- `ai-shell-web-ui` - Shell Web UI
|
|
33
33
|
- `optima-store` - 商城前端(仅 Stage)
|
|
34
|
+
- `billing` - 计费服务
|
|
35
|
+
- `browser-backend` - 浏览器自动化服务
|
|
36
|
+
- `optima-generation` - 内容生成服务
|
|
37
|
+
- `optima-generation-worker` - 内容生成 Worker
|
|
34
38
|
- `pgbouncer` - 连接池(仅 Stage)
|
|
35
39
|
- `environment` (可选): 环境,默认 stage
|
|
36
40
|
- `stage` - Stage 预发布环境(默认,更安全)
|
|
@@ -93,6 +97,10 @@ aws ecs update-service \
|
|
|
93
97
|
- `optima-scout-stage`
|
|
94
98
|
- `ai-shell-web-ui-stage`
|
|
95
99
|
- `optima-store-stage`
|
|
100
|
+
- `billing-stage`
|
|
101
|
+
- `browser-backend-stage`
|
|
102
|
+
- `optima-generation-stage`
|
|
103
|
+
- `optima-generation-worker-stage`
|
|
96
104
|
- `pgbouncer-stage`
|
|
97
105
|
|
|
98
106
|
### 2. Prod 环境重启(environment = "prod")
|
|
@@ -126,6 +134,10 @@ aws ecs update-service \
|
|
|
126
134
|
- `bi-dashboard-prod`
|
|
127
135
|
- `optima-scout-prod`
|
|
128
136
|
- `ai-shell-web-ui-prod`
|
|
137
|
+
- `billing-prod`
|
|
138
|
+
- `browser-backend-prod`
|
|
139
|
+
- `optima-generation-prod`
|
|
140
|
+
- `optima-generation-worker-prod`
|
|
129
141
|
|
|
130
142
|
**注意**: `optima-store` 和 `pgbouncer` 仅在 Stage 环境部署
|
|
131
143
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebSearch",
|
|
5
|
+
"WebFetch(domain:code.claude.com)",
|
|
6
|
+
"WebFetch(domain:platform.claude.com)",
|
|
7
|
+
"WebFetch(domain:github.com)",
|
|
8
|
+
"Bash(gh repo view:*)",
|
|
9
|
+
"Bash(gh repo clone:*)",
|
|
10
|
+
"Bash(gh repo list:*)",
|
|
11
|
+
"Read(//private/tmp/optima-docs/**)",
|
|
12
|
+
"Read(//tmp/optima-docs/**)",
|
|
13
|
+
"Bash(git init:*)",
|
|
14
|
+
"Bash(gh repo create:*)",
|
|
15
|
+
"Read(//private/tmp/optima-workspace/**)",
|
|
16
|
+
"Read(//tmp/optima-workspace/**)",
|
|
17
|
+
"Read(//tmp/optima-workspace/.claude/commands/**)",
|
|
18
|
+
"Bash(git add:*)",
|
|
19
|
+
"Bash(git push:*)",
|
|
20
|
+
"Bash(find:*)",
|
|
21
|
+
"Bash(git commit:*)",
|
|
22
|
+
"Bash(aws logs get-log-events:*)",
|
|
23
|
+
"Bash(npm install:*)",
|
|
24
|
+
"Bash(optima-dev-skills:*)",
|
|
25
|
+
"Bash(optima-generate-test-token:*)",
|
|
26
|
+
"Bash(optima-query-db:*)",
|
|
27
|
+
"Bash(gh variable set:*)",
|
|
28
|
+
"Bash(npm publish:*)",
|
|
29
|
+
"Bash(python3:*)",
|
|
30
|
+
"Bash(gh api:*)",
|
|
31
|
+
"Bash(curl -s http://auth.optima.chat/openapi.json)",
|
|
32
|
+
"Bash(curl -s https://auth.optima.chat/openapi.json)",
|
|
33
|
+
"Bash(cat:*)",
|
|
34
|
+
"Bash(node /Users/verypro/optima-dev-skills/scripts/install.js:*)",
|
|
35
|
+
"Bash(aws logs tail:*)",
|
|
36
|
+
"Bash(grep:*)",
|
|
37
|
+
"Bash(npm view:*)",
|
|
38
|
+
"Bash(npm version:*)",
|
|
39
|
+
"Bash(git checkout:*)",
|
|
40
|
+
"Bash(git pull:*)",
|
|
41
|
+
"Bash(node scripts/install.js:*)"
|
|
42
|
+
],
|
|
43
|
+
"deny": [],
|
|
44
|
+
"ask": []
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -191,6 +191,7 @@ INFO - Database query took 3200ms: SELECT * FROM products WHERE...
|
|
|
191
191
|
| `optima-store` | 商城前端 | - | ✓ | - |
|
|
192
192
|
| `commerce-rq-worker` | RQ 后台任务 | - | ✓ | ✓ |
|
|
193
193
|
| `commerce-rq-scheduler` | RQ 定时调度 | - | ✓ | ✓ |
|
|
194
|
+
| `optima-logistics` | 物流服务 | - | ✓ | ✓ |
|
|
194
195
|
|
|
195
196
|
## 最佳实践
|
|
196
197
|
|
|
@@ -76,6 +76,7 @@ optima-query-db commerce-backend "SELECT status, COUNT(*) FROM orders GROUP BY s
|
|
|
76
76
|
- `agentic-chat` - AI 聊天数据库
|
|
77
77
|
- `bi-backend` - BI 后端数据库
|
|
78
78
|
- `session-gateway` - AI Shell 网关数据库
|
|
79
|
+
- `optima-logistics` - 物流服务数据库
|
|
79
80
|
|
|
80
81
|
### 常用查询示例
|
|
81
82
|
|
|
@@ -195,6 +196,7 @@ optima-query-db commerce-backend "SELECT status, COUNT(*) FROM orders GROUP BY s
|
|
|
195
196
|
| agentic-chat | `CHAT_DB_USER` | `CHAT_DB_PASSWORD` |
|
|
196
197
|
| bi-backend | `BI_DB_USER` | `BI_DB_PASSWORD` |
|
|
197
198
|
| session-gateway | `AI_SHELL_DB_USER` | `AI_SHELL_DB_PASSWORD` |
|
|
199
|
+
| optima-logistics | `LOGISTICS_DB_USER` | `LOGISTICS_DB_PASSWORD` |
|
|
198
200
|
|
|
199
201
|
### RDS 连接
|
|
200
202
|
|
|
@@ -71,6 +71,7 @@ allowed-tools: ["Bash"]
|
|
|
71
71
|
| ai-shell-web-ui | ai-shell-web-ui-stage | Shell Web UI |
|
|
72
72
|
| optima-store | optima-store-stage | 商城前端 |
|
|
73
73
|
| pgbouncer | pgbouncer-stage | 数据库连接池 |
|
|
74
|
+
| optima-logistics | optima-logistics-stage | 物流服务 |
|
|
74
75
|
|
|
75
76
|
### Prod 环境
|
|
76
77
|
|
|
@@ -90,6 +91,7 @@ allowed-tools: ["Bash"]
|
|
|
90
91
|
| bi-dashboard | bi-dashboard-prod | BI 仪表板 |
|
|
91
92
|
| optima-scout | optima-scout-prod | 产品研究工具 |
|
|
92
93
|
| ai-shell-web-ui | ai-shell-web-ui-prod | Shell Web UI |
|
|
94
|
+
| optima-logistics | optima-logistics-prod | 物流服务 |
|
|
93
95
|
|
|
94
96
|
**注意**: `optima-store` 和 `pgbouncer` 仅在 Stage 环境部署
|
|
95
97
|
|
package/bin/cli.js
CHANGED
|
@@ -37,7 +37,8 @@ switch (command) {
|
|
|
37
37
|
log(' /logs mcp-host 200 prod Prod, 200 lines', 'cyan');
|
|
38
38
|
|
|
39
39
|
log('\nSupported Services:', 'yellow');
|
|
40
|
-
log(' commerce-backend user-auth mcp-host agentic-chat', 'cyan');
|
|
40
|
+
log(' commerce-backend user-auth mcp-host agentic-chat optima-logistics', 'cyan');
|
|
41
|
+
log(' optima-scout billing browser-backend optima-generation', 'cyan');
|
|
41
42
|
|
|
42
43
|
log('\nEnvironments:', 'yellow');
|
|
43
44
|
log(' stage (default) prod', 'cyan');
|
package/bin/helpers/query-db.ts
CHANGED
|
@@ -42,6 +42,26 @@ const SERVICE_DB_MAP = {
|
|
|
42
42
|
ci: null, // CI 环境暂无 session-gateway 数据库
|
|
43
43
|
stage: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_shell' },
|
|
44
44
|
prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
|
|
45
|
+
},
|
|
46
|
+
'optima-logistics': {
|
|
47
|
+
ci: null,
|
|
48
|
+
stage: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_stage_logistics' },
|
|
49
|
+
prod: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_logistics' }
|
|
50
|
+
},
|
|
51
|
+
'billing': {
|
|
52
|
+
ci: null,
|
|
53
|
+
stage: { databaseUrlPath: '/services/billing', databaseUrlKey: 'DATABASE_URL' },
|
|
54
|
+
prod: { databaseUrlPath: '/services/billing', databaseUrlKey: 'DATABASE_URL' }
|
|
55
|
+
},
|
|
56
|
+
'browser-backend': {
|
|
57
|
+
ci: null,
|
|
58
|
+
stage: { databaseUrlPath: '/services/browser-backend', databaseUrlKey: 'DATABASE_URL' },
|
|
59
|
+
prod: { databaseUrlPath: '/services/browser-backend', databaseUrlKey: 'DATABASE_URL' }
|
|
60
|
+
},
|
|
61
|
+
'optima-generation': {
|
|
62
|
+
ci: null,
|
|
63
|
+
stage: { databaseUrlPath: '/services/optima-generation', databaseUrlKey: 'DATABASE_URL' },
|
|
64
|
+
prod: { databaseUrlPath: '/services/optima-generation', databaseUrlKey: 'DATABASE_URL' }
|
|
45
65
|
}
|
|
46
66
|
};
|
|
47
67
|
|
|
@@ -54,6 +74,21 @@ const RDS_HOSTS = {
|
|
|
54
74
|
// 统一使用 BI Data ARM Host 作为跳板机
|
|
55
75
|
const EC2_HOST = '3.0.210.113';
|
|
56
76
|
|
|
77
|
+
function parseDatabaseUrl(url: string): { user: string; password: string; host: string; port: number; database: string } {
|
|
78
|
+
// postgresql://user:password@host:port/database?params
|
|
79
|
+
const match = url.match(/^postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/([^?]+)/);
|
|
80
|
+
if (!match) {
|
|
81
|
+
throw new Error(`Failed to parse DATABASE_URL: ${url}`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
user: decodeURIComponent(match[1]),
|
|
85
|
+
password: decodeURIComponent(match[2]),
|
|
86
|
+
host: match[3],
|
|
87
|
+
port: parseInt(match[4]),
|
|
88
|
+
database: match[5]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
57
92
|
function getGitHubVariable(name: string): string {
|
|
58
93
|
return execSync(`gh variable get ${name} -R Optima-Chat/optima-dev-skills`, { encoding: 'utf-8' }).trim();
|
|
59
94
|
}
|
|
@@ -174,7 +209,7 @@ async function main() {
|
|
|
174
209
|
if (args.length < 2) {
|
|
175
210
|
console.error('Usage: query-db.ts <service> <sql> [environment]');
|
|
176
211
|
console.error('');
|
|
177
|
-
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway');
|
|
212
|
+
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, optima-logistics, billing, browser-backend, optima-generation');
|
|
178
213
|
console.error('Environments: ci (default), stage, prod');
|
|
179
214
|
console.error('');
|
|
180
215
|
console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
|
|
@@ -223,19 +258,42 @@ async function main() {
|
|
|
223
258
|
const token = getInfisicalToken(infisicalConfig);
|
|
224
259
|
console.log('✓ Obtained Infisical access token');
|
|
225
260
|
|
|
226
|
-
// 数据库凭证存储在 Infisical 的 /shared-secrets/database-users 路径
|
|
227
|
-
// Stage 从 staging 环境读取,Prod 从 prod 环境读取
|
|
228
261
|
const infisicalEnv = environment === 'stage' ? 'staging' : 'prod';
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
262
|
+
let dbUser: string;
|
|
263
|
+
let dbPassword: string;
|
|
264
|
+
let dbHost: string;
|
|
265
|
+
let database: string;
|
|
266
|
+
|
|
267
|
+
if ('databaseUrlPath' in (serviceConfig as any)) {
|
|
268
|
+
// 从服务路径获取 DATABASE_URL 并解析
|
|
269
|
+
const { databaseUrlPath, databaseUrlKey } = serviceConfig as any;
|
|
270
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, databaseUrlPath);
|
|
271
|
+
console.log(`✓ Retrieved DATABASE_URL from Infisical (path: ${databaseUrlPath})`);
|
|
272
|
+
|
|
273
|
+
const databaseUrl = secrets[databaseUrlKey];
|
|
274
|
+
if (!databaseUrl) {
|
|
275
|
+
throw new Error(`DATABASE_URL not found in Infisical at ${databaseUrlPath}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const parsed = parseDatabaseUrl(databaseUrl);
|
|
279
|
+
dbUser = parsed.user;
|
|
280
|
+
dbPassword = parsed.password;
|
|
281
|
+
dbHost = parsed.host;
|
|
282
|
+
database = parsed.database;
|
|
283
|
+
} else {
|
|
284
|
+
// 从 shared-secrets/database-users 获取凭证
|
|
285
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, '/shared-secrets/database-users');
|
|
286
|
+
console.log('✓ Retrieved database credentials from Infisical');
|
|
287
|
+
|
|
288
|
+
const { userKey, passwordKey } = serviceConfig as any;
|
|
289
|
+
database = (serviceConfig as any).database;
|
|
290
|
+
dbHost = RDS_HOSTS[environment as 'stage' | 'prod'];
|
|
291
|
+
dbUser = secrets[userKey];
|
|
292
|
+
dbPassword = secrets[passwordKey];
|
|
293
|
+
|
|
294
|
+
if (!dbUser || !dbPassword) {
|
|
295
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
296
|
+
}
|
|
239
297
|
}
|
|
240
298
|
|
|
241
299
|
const localPort = environment === 'stage' ? 15432 : 15433;
|
package/bin/helpers/show-env.ts
CHANGED
|
@@ -18,7 +18,12 @@ const SUPPORTED_SERVICES = [
|
|
|
18
18
|
'session-gateway',
|
|
19
19
|
'optima-store',
|
|
20
20
|
'optima-scout',
|
|
21
|
-
'mcp-host'
|
|
21
|
+
'mcp-host',
|
|
22
|
+
'shopify-backend',
|
|
23
|
+
'optima-logistics',
|
|
24
|
+
'billing',
|
|
25
|
+
'browser-backend',
|
|
26
|
+
'optima-generation'
|
|
22
27
|
];
|
|
23
28
|
|
|
24
29
|
// 环境到 Infisical environment 的映射
|
|
File without changes
|
|
@@ -61,6 +61,26 @@ const SERVICE_DB_MAP = {
|
|
|
61
61
|
ci: null, // CI 环境暂无 session-gateway 数据库
|
|
62
62
|
stage: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_shell' },
|
|
63
63
|
prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
|
|
64
|
+
},
|
|
65
|
+
'optima-logistics': {
|
|
66
|
+
ci: null,
|
|
67
|
+
stage: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_stage_logistics' },
|
|
68
|
+
prod: { userKey: 'LOGISTICS_DB_USER', passwordKey: 'LOGISTICS_DB_PASSWORD', database: 'optima_logistics' }
|
|
69
|
+
},
|
|
70
|
+
'billing': {
|
|
71
|
+
ci: null,
|
|
72
|
+
stage: { databaseUrlPath: '/services/billing', databaseUrlKey: 'DATABASE_URL' },
|
|
73
|
+
prod: { databaseUrlPath: '/services/billing', databaseUrlKey: 'DATABASE_URL' }
|
|
74
|
+
},
|
|
75
|
+
'browser-backend': {
|
|
76
|
+
ci: null,
|
|
77
|
+
stage: { databaseUrlPath: '/services/browser-backend', databaseUrlKey: 'DATABASE_URL' },
|
|
78
|
+
prod: { databaseUrlPath: '/services/browser-backend', databaseUrlKey: 'DATABASE_URL' }
|
|
79
|
+
},
|
|
80
|
+
'optima-generation': {
|
|
81
|
+
ci: null,
|
|
82
|
+
stage: { databaseUrlPath: '/services/optima-generation', databaseUrlKey: 'DATABASE_URL' },
|
|
83
|
+
prod: { databaseUrlPath: '/services/optima-generation', databaseUrlKey: 'DATABASE_URL' }
|
|
64
84
|
}
|
|
65
85
|
};
|
|
66
86
|
// Stage 和 Prod 独立的 RDS 实例
|
|
@@ -70,6 +90,20 @@ const RDS_HOSTS = {
|
|
|
70
90
|
};
|
|
71
91
|
// 统一使用 BI Data ARM Host 作为跳板机
|
|
72
92
|
const EC2_HOST = '3.0.210.113';
|
|
93
|
+
function parseDatabaseUrl(url) {
|
|
94
|
+
// postgresql://user:password@host:port/database?params
|
|
95
|
+
const match = url.match(/^postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/([^?]+)/);
|
|
96
|
+
if (!match) {
|
|
97
|
+
throw new Error(`Failed to parse DATABASE_URL: ${url}`);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
user: decodeURIComponent(match[1]),
|
|
101
|
+
password: decodeURIComponent(match[2]),
|
|
102
|
+
host: match[3],
|
|
103
|
+
port: parseInt(match[4]),
|
|
104
|
+
database: match[5]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
73
107
|
function getGitHubVariable(name) {
|
|
74
108
|
return (0, child_process_1.execSync)(`gh variable get ${name} -R Optima-Chat/optima-dev-skills`, { encoding: 'utf-8' }).trim();
|
|
75
109
|
}
|
|
@@ -165,7 +199,7 @@ async function main() {
|
|
|
165
199
|
if (args.length < 2) {
|
|
166
200
|
console.error('Usage: query-db.ts <service> <sql> [environment]');
|
|
167
201
|
console.error('');
|
|
168
|
-
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway');
|
|
202
|
+
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, optima-logistics, billing, browser-backend, optima-generation');
|
|
169
203
|
console.error('Environments: ci (default), stage, prod');
|
|
170
204
|
console.error('');
|
|
171
205
|
console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
|
|
@@ -201,17 +235,38 @@ async function main() {
|
|
|
201
235
|
console.log('✓ Loaded Infisical config from GitHub Variables');
|
|
202
236
|
const token = getInfisicalToken(infisicalConfig);
|
|
203
237
|
console.log('✓ Obtained Infisical access token');
|
|
204
|
-
// 数据库凭证存储在 Infisical 的 /shared-secrets/database-users 路径
|
|
205
|
-
// Stage 从 staging 环境读取,Prod 从 prod 环境读取
|
|
206
238
|
const infisicalEnv = environment === 'stage' ? 'staging' : 'prod';
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
239
|
+
let dbUser;
|
|
240
|
+
let dbPassword;
|
|
241
|
+
let dbHost;
|
|
242
|
+
let database;
|
|
243
|
+
if ('databaseUrlPath' in serviceConfig) {
|
|
244
|
+
// 从服务路径获取 DATABASE_URL 并解析
|
|
245
|
+
const { databaseUrlPath, databaseUrlKey } = serviceConfig;
|
|
246
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, databaseUrlPath);
|
|
247
|
+
console.log(`✓ Retrieved DATABASE_URL from Infisical (path: ${databaseUrlPath})`);
|
|
248
|
+
const databaseUrl = secrets[databaseUrlKey];
|
|
249
|
+
if (!databaseUrl) {
|
|
250
|
+
throw new Error(`DATABASE_URL not found in Infisical at ${databaseUrlPath}`);
|
|
251
|
+
}
|
|
252
|
+
const parsed = parseDatabaseUrl(databaseUrl);
|
|
253
|
+
dbUser = parsed.user;
|
|
254
|
+
dbPassword = parsed.password;
|
|
255
|
+
dbHost = parsed.host;
|
|
256
|
+
database = parsed.database;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// 从 shared-secrets/database-users 获取凭证
|
|
260
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, '/shared-secrets/database-users');
|
|
261
|
+
console.log('✓ Retrieved database credentials from Infisical');
|
|
262
|
+
const { userKey, passwordKey } = serviceConfig;
|
|
263
|
+
database = serviceConfig.database;
|
|
264
|
+
dbHost = RDS_HOSTS[environment];
|
|
265
|
+
dbUser = secrets[userKey];
|
|
266
|
+
dbPassword = secrets[passwordKey];
|
|
267
|
+
if (!dbUser || !dbPassword) {
|
|
268
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
269
|
+
}
|
|
215
270
|
}
|
|
216
271
|
const localPort = environment === 'stage' ? 15432 : 15433;
|
|
217
272
|
setupSSHTunnel(EC2_HOST, dbHost, localPort);
|
|
@@ -11,7 +11,12 @@ const SUPPORTED_SERVICES = [
|
|
|
11
11
|
'session-gateway',
|
|
12
12
|
'optima-store',
|
|
13
13
|
'optima-scout',
|
|
14
|
-
'mcp-host'
|
|
14
|
+
'mcp-host',
|
|
15
|
+
'shopify-backend',
|
|
16
|
+
'optima-logistics',
|
|
17
|
+
'billing',
|
|
18
|
+
'browser-backend',
|
|
19
|
+
'optima-generation'
|
|
15
20
|
];
|
|
16
21
|
// 环境到 Infisical environment 的映射
|
|
17
22
|
const ENV_MAP = {
|