@optima-chat/dev-skills 0.7.8 → 0.7.10
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/bin/helpers/query-db.ts +60 -18
- package/dist/bin/helpers/query-db.js +53 -18
- package/package.json +1 -1
package/bin/helpers/query-db.ts
CHANGED
|
@@ -20,31 +20,40 @@ interface DatabaseConfig {
|
|
|
20
20
|
const SERVICE_DB_MAP = {
|
|
21
21
|
'commerce-backend': {
|
|
22
22
|
ci: { container: 'commerce-postgres', user: 'commerce', password: 'commerce123', database: 'commerce' },
|
|
23
|
-
stage: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: '
|
|
23
|
+
stage: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: 'optima_commerce' },
|
|
24
24
|
prod: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: 'optima_commerce' }
|
|
25
25
|
},
|
|
26
26
|
'user-auth': {
|
|
27
27
|
ci: { container: 'user-auth-postgres-1', user: 'userauth', password: 'password123', database: 'userauth' },
|
|
28
|
-
stage: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: '
|
|
28
|
+
stage: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: 'optima_auth' },
|
|
29
29
|
prod: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: 'optima_auth' }
|
|
30
30
|
},
|
|
31
|
-
'mcp-host': {
|
|
32
|
-
ci: { container: 'mcp-host-db-1', user: 'mcp_user', password: 'mcp_password', database: 'mcp_host' },
|
|
33
|
-
stage: { userKey: 'MCP_DB_USER', passwordKey: 'MCP_DB_PASSWORD', database: 'optima_stage_mcp' },
|
|
34
|
-
prod: { userKey: 'MCP_DB_USER', passwordKey: 'MCP_DB_PASSWORD', database: 'optima_mcp' }
|
|
35
|
-
},
|
|
36
31
|
'agentic-chat': {
|
|
37
32
|
ci: { container: 'optima-postgres', user: 'postgres', password: 'postgres123', database: 'optima_chat' },
|
|
38
|
-
stage: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: '
|
|
33
|
+
stage: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: 'optima_chat' },
|
|
39
34
|
prod: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: 'optima_chat' }
|
|
35
|
+
},
|
|
36
|
+
'bi-backend': {
|
|
37
|
+
ci: null, // CI 环境暂无 BI 数据库
|
|
38
|
+
stage: { userKey: 'BI_DB_USER', passwordKey: 'BI_DB_PASSWORD', database: 'optima_bi' },
|
|
39
|
+
prod: { userKey: 'BI_DB_USER', passwordKey: 'BI_DB_PASSWORD', database: 'optima_bi' }
|
|
40
|
+
},
|
|
41
|
+
'session-gateway': {
|
|
42
|
+
ci: null, // CI 环境暂无 session-gateway 数据库
|
|
43
|
+
stage: { userKey: 'SHELL_DB_USER', passwordKey: 'SHELL_DB_PASSWORD', database: 'optima_shell' },
|
|
44
|
+
prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
|
|
40
45
|
}
|
|
41
46
|
};
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
// Stage 和 Prod 独立的 RDS 实例
|
|
49
|
+
const RDS_HOSTS = {
|
|
50
|
+
stage: 'optima-stage-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com',
|
|
51
|
+
prod: 'optima-prod-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com'
|
|
46
52
|
};
|
|
47
53
|
|
|
54
|
+
// 统一使用 Shared EC2 作为跳板机
|
|
55
|
+
const EC2_HOST = '13.251.46.219';
|
|
56
|
+
|
|
48
57
|
function getGitHubVariable(name: string): string {
|
|
49
58
|
return execSync(`gh variable get ${name} -R Optima-Chat/optima-dev-skills`, { encoding: 'utf-8' }).trim();
|
|
50
59
|
}
|
|
@@ -165,7 +174,7 @@ async function main() {
|
|
|
165
174
|
if (args.length < 2) {
|
|
166
175
|
console.error('Usage: query-db.ts <service> <sql> [environment]');
|
|
167
176
|
console.error('');
|
|
168
|
-
console.error('Services: commerce-backend, user-auth,
|
|
177
|
+
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway');
|
|
169
178
|
console.error('Environments: ci (default), stage, prod');
|
|
170
179
|
console.error('');
|
|
171
180
|
console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
|
|
@@ -182,6 +191,14 @@ async function main() {
|
|
|
182
191
|
|
|
183
192
|
const serviceConfig = SERVICE_DB_MAP[service as keyof typeof SERVICE_DB_MAP][environment as 'ci' | 'stage' | 'prod'];
|
|
184
193
|
|
|
194
|
+
if (!serviceConfig) {
|
|
195
|
+
console.error(`Service ${service} is not available in ${environment.toUpperCase()} environment.`);
|
|
196
|
+
if (environment === 'ci') {
|
|
197
|
+
console.error('Try using stage or prod environment instead.');
|
|
198
|
+
}
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
185
202
|
console.log(`\n🔍 Querying ${service} (${environment.toUpperCase()})...`);
|
|
186
203
|
|
|
187
204
|
if (environment === 'ci') {
|
|
@@ -197,27 +214,52 @@ async function main() {
|
|
|
197
214
|
{ encoding: 'utf-8' }
|
|
198
215
|
);
|
|
199
216
|
|
|
217
|
+
console.log('\n' + result);
|
|
218
|
+
} else if (environment === 'stage') {
|
|
219
|
+
// Stage 环境:直连 RDS(Stage RDS 在公有子网,可以本地直连)
|
|
220
|
+
const infisicalConfig = getInfisicalConfig();
|
|
221
|
+
console.log('✓ Loaded Infisical config from GitHub Variables');
|
|
222
|
+
|
|
223
|
+
const token = getInfisicalToken(infisicalConfig);
|
|
224
|
+
console.log('✓ Obtained Infisical access token');
|
|
225
|
+
|
|
226
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, 'staging');
|
|
227
|
+
console.log('✓ Retrieved database credentials from Infisical');
|
|
228
|
+
|
|
229
|
+
const { userKey, passwordKey, database } = serviceConfig as any;
|
|
230
|
+
const dbHost = RDS_HOSTS.stage;
|
|
231
|
+
const dbUser = secrets[userKey];
|
|
232
|
+
const dbPassword = secrets[passwordKey];
|
|
233
|
+
|
|
234
|
+
if (!dbUser || !dbPassword) {
|
|
235
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const result = queryDatabase(dbHost, 5432, dbUser, dbPassword, database, sql);
|
|
200
239
|
console.log('\n' + result);
|
|
201
240
|
} else {
|
|
202
|
-
//
|
|
241
|
+
// Prod 环境:通过 SSH 隧道访问 RDS(Prod RDS 在私有子网)
|
|
203
242
|
const infisicalConfig = getInfisicalConfig();
|
|
204
243
|
console.log('✓ Loaded Infisical config from GitHub Variables');
|
|
205
244
|
|
|
206
245
|
const token = getInfisicalToken(infisicalConfig);
|
|
207
246
|
console.log('✓ Obtained Infisical access token');
|
|
208
247
|
|
|
209
|
-
const secrets = getInfisicalSecrets(infisicalConfig, token,
|
|
248
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, 'prod');
|
|
210
249
|
console.log('✓ Retrieved database credentials from Infisical');
|
|
211
250
|
|
|
212
251
|
const { userKey, passwordKey, database } = serviceConfig as any;
|
|
213
|
-
const dbHost =
|
|
252
|
+
const dbHost = RDS_HOSTS.prod;
|
|
214
253
|
const dbUser = secrets[userKey];
|
|
215
254
|
const dbPassword = secrets[passwordKey];
|
|
216
255
|
|
|
217
|
-
|
|
218
|
-
|
|
256
|
+
if (!dbUser || !dbPassword) {
|
|
257
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const localPort = 15433;
|
|
219
261
|
|
|
220
|
-
setupSSHTunnel(
|
|
262
|
+
setupSSHTunnel(EC2_HOST, dbHost, localPort);
|
|
221
263
|
|
|
222
264
|
// 等待隧道建立
|
|
223
265
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
@@ -39,29 +39,37 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const SERVICE_DB_MAP = {
|
|
40
40
|
'commerce-backend': {
|
|
41
41
|
ci: { container: 'commerce-postgres', user: 'commerce', password: 'commerce123', database: 'commerce' },
|
|
42
|
-
stage: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: '
|
|
42
|
+
stage: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: 'optima_commerce' },
|
|
43
43
|
prod: { userKey: 'COMMERCE_DB_USER', passwordKey: 'COMMERCE_DB_PASSWORD', database: 'optima_commerce' }
|
|
44
44
|
},
|
|
45
45
|
'user-auth': {
|
|
46
46
|
ci: { container: 'user-auth-postgres-1', user: 'userauth', password: 'password123', database: 'userauth' },
|
|
47
|
-
stage: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: '
|
|
47
|
+
stage: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: 'optima_auth' },
|
|
48
48
|
prod: { userKey: 'AUTH_DB_USER', passwordKey: 'AUTH_DB_PASSWORD', database: 'optima_auth' }
|
|
49
49
|
},
|
|
50
|
-
'mcp-host': {
|
|
51
|
-
ci: { container: 'mcp-host-db-1', user: 'mcp_user', password: 'mcp_password', database: 'mcp_host' },
|
|
52
|
-
stage: { userKey: 'MCP_DB_USER', passwordKey: 'MCP_DB_PASSWORD', database: 'optima_stage_mcp' },
|
|
53
|
-
prod: { userKey: 'MCP_DB_USER', passwordKey: 'MCP_DB_PASSWORD', database: 'optima_mcp' }
|
|
54
|
-
},
|
|
55
50
|
'agentic-chat': {
|
|
56
51
|
ci: { container: 'optima-postgres', user: 'postgres', password: 'postgres123', database: 'optima_chat' },
|
|
57
|
-
stage: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: '
|
|
52
|
+
stage: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: 'optima_chat' },
|
|
58
53
|
prod: { userKey: 'CHAT_DB_USER', passwordKey: 'CHAT_DB_PASSWORD', database: 'optima_chat' }
|
|
54
|
+
},
|
|
55
|
+
'bi-backend': {
|
|
56
|
+
ci: null, // CI 环境暂无 BI 数据库
|
|
57
|
+
stage: { userKey: 'BI_DB_USER', passwordKey: 'BI_DB_PASSWORD', database: 'optima_bi' },
|
|
58
|
+
prod: { userKey: 'BI_DB_USER', passwordKey: 'BI_DB_PASSWORD', database: 'optima_bi' }
|
|
59
|
+
},
|
|
60
|
+
'session-gateway': {
|
|
61
|
+
ci: null, // CI 环境暂无 session-gateway 数据库
|
|
62
|
+
stage: { userKey: 'SHELL_DB_USER', passwordKey: 'SHELL_DB_PASSWORD', database: 'optima_shell' },
|
|
63
|
+
prod: { userKey: 'AI_SHELL_DB_USER', passwordKey: 'AI_SHELL_DB_PASSWORD', database: 'optima_ai_shell' }
|
|
59
64
|
}
|
|
60
65
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
// Stage 和 Prod 独立的 RDS 实例
|
|
67
|
+
const RDS_HOSTS = {
|
|
68
|
+
stage: 'optima-stage-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com',
|
|
69
|
+
prod: 'optima-prod-postgres.ctg866o0ehac.ap-southeast-1.rds.amazonaws.com'
|
|
64
70
|
};
|
|
71
|
+
// 统一使用 Shared EC2 作为跳板机
|
|
72
|
+
const EC2_HOST = '13.251.46.219';
|
|
65
73
|
function getGitHubVariable(name) {
|
|
66
74
|
return (0, child_process_1.execSync)(`gh variable get ${name} -R Optima-Chat/optima-dev-skills`, { encoding: 'utf-8' }).trim();
|
|
67
75
|
}
|
|
@@ -157,7 +165,7 @@ async function main() {
|
|
|
157
165
|
if (args.length < 2) {
|
|
158
166
|
console.error('Usage: query-db.ts <service> <sql> [environment]');
|
|
159
167
|
console.error('');
|
|
160
|
-
console.error('Services: commerce-backend, user-auth,
|
|
168
|
+
console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway');
|
|
161
169
|
console.error('Environments: ci (default), stage, prod');
|
|
162
170
|
console.error('');
|
|
163
171
|
console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
|
|
@@ -170,6 +178,13 @@ async function main() {
|
|
|
170
178
|
process.exit(1);
|
|
171
179
|
}
|
|
172
180
|
const serviceConfig = SERVICE_DB_MAP[service][environment];
|
|
181
|
+
if (!serviceConfig) {
|
|
182
|
+
console.error(`Service ${service} is not available in ${environment.toUpperCase()} environment.`);
|
|
183
|
+
if (environment === 'ci') {
|
|
184
|
+
console.error('Try using stage or prod environment instead.');
|
|
185
|
+
}
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
173
188
|
console.log(`\n🔍 Querying ${service} (${environment.toUpperCase()})...`);
|
|
174
189
|
if (environment === 'ci') {
|
|
175
190
|
// CI 环境:通过 SSH + Docker Exec
|
|
@@ -180,21 +195,41 @@ async function main() {
|
|
|
180
195
|
const result = (0, child_process_1.execSync)(`sshpass -p "${ciPassword}" ssh -o StrictHostKeyChecking=no ${ciUser}@${ciHost} "docker exec ${container} psql -U ${user} -d ${database} -c \\"${sql}\\""`, { encoding: 'utf-8' });
|
|
181
196
|
console.log('\n' + result);
|
|
182
197
|
}
|
|
198
|
+
else if (environment === 'stage') {
|
|
199
|
+
// Stage 环境:直连 RDS(Stage RDS 在公有子网,可以本地直连)
|
|
200
|
+
const infisicalConfig = getInfisicalConfig();
|
|
201
|
+
console.log('✓ Loaded Infisical config from GitHub Variables');
|
|
202
|
+
const token = getInfisicalToken(infisicalConfig);
|
|
203
|
+
console.log('✓ Obtained Infisical access token');
|
|
204
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, 'staging');
|
|
205
|
+
console.log('✓ Retrieved database credentials from Infisical');
|
|
206
|
+
const { userKey, passwordKey, database } = serviceConfig;
|
|
207
|
+
const dbHost = RDS_HOSTS.stage;
|
|
208
|
+
const dbUser = secrets[userKey];
|
|
209
|
+
const dbPassword = secrets[passwordKey];
|
|
210
|
+
if (!dbUser || !dbPassword) {
|
|
211
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
212
|
+
}
|
|
213
|
+
const result = queryDatabase(dbHost, 5432, dbUser, dbPassword, database, sql);
|
|
214
|
+
console.log('\n' + result);
|
|
215
|
+
}
|
|
183
216
|
else {
|
|
184
|
-
//
|
|
217
|
+
// Prod 环境:通过 SSH 隧道访问 RDS(Prod RDS 在私有子网)
|
|
185
218
|
const infisicalConfig = getInfisicalConfig();
|
|
186
219
|
console.log('✓ Loaded Infisical config from GitHub Variables');
|
|
187
220
|
const token = getInfisicalToken(infisicalConfig);
|
|
188
221
|
console.log('✓ Obtained Infisical access token');
|
|
189
|
-
const secrets = getInfisicalSecrets(infisicalConfig, token,
|
|
222
|
+
const secrets = getInfisicalSecrets(infisicalConfig, token, 'prod');
|
|
190
223
|
console.log('✓ Retrieved database credentials from Infisical');
|
|
191
224
|
const { userKey, passwordKey, database } = serviceConfig;
|
|
192
|
-
const dbHost =
|
|
225
|
+
const dbHost = RDS_HOSTS.prod;
|
|
193
226
|
const dbUser = secrets[userKey];
|
|
194
227
|
const dbPassword = secrets[passwordKey];
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
228
|
+
if (!dbUser || !dbPassword) {
|
|
229
|
+
throw new Error(`Database credentials not found in Infisical for ${service}. Keys: ${userKey}, ${passwordKey}`);
|
|
230
|
+
}
|
|
231
|
+
const localPort = 15433;
|
|
232
|
+
setupSSHTunnel(EC2_HOST, dbHost, localPort);
|
|
198
233
|
// 等待隧道建立
|
|
199
234
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
200
235
|
const result = queryDatabase('localhost', localPort, dbUser, dbPassword, database, sql);
|