@geekmidas/cli 1.10.41 → 1.11.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/CHANGELOG.md +17 -0
- package/README.md +14 -0
- package/dist/{CachedStateProvider-37pG1HBx.cjs → CachedStateProvider-BdX7klRc.cjs} +1 -1
- package/dist/{CachedStateProvider-C8A-cCm5.mjs → CachedStateProvider-CzyFct42.mjs} +1 -1
- package/dist/{CachedStateProvider-C8A-cCm5.mjs.map → CachedStateProvider-CzyFct42.mjs.map} +1 -1
- package/dist/CachedStateProvider-GcGziXh1.mjs +3 -0
- package/dist/{CachedStateProvider-CxsXOKkg.cjs → CachedStateProvider-klLWZXWF.cjs} +1 -1
- package/dist/{CachedStateProvider-CxsXOKkg.cjs.map → CachedStateProvider-klLWZXWF.cjs.map} +1 -1
- package/dist/{HostingerProvider-ANWchdiK.mjs → HostingerProvider-B2ZYz7Yc.mjs} +1 -1
- package/dist/{HostingerProvider-ANWchdiK.mjs.map → HostingerProvider-B2ZYz7Yc.mjs.map} +1 -1
- package/dist/{HostingerProvider-5KYmwoK2.cjs → HostingerProvider-CrHfsgz9.cjs} +1 -1
- package/dist/{HostingerProvider-5KYmwoK2.cjs.map → HostingerProvider-CrHfsgz9.cjs.map} +1 -1
- package/dist/{LocalStateProvider-Dp0KkRcw.mjs → LocalStateProvider-CP49rjzt.mjs} +1 -1
- package/dist/{LocalStateProvider-Dp0KkRcw.mjs.map → LocalStateProvider-CP49rjzt.mjs.map} +1 -1
- package/dist/{LocalStateProvider-CLifRC0Y.cjs → LocalStateProvider-CalzNfNA.cjs} +1 -1
- package/dist/{LocalStateProvider-CLifRC0Y.cjs.map → LocalStateProvider-CalzNfNA.cjs.map} +1 -1
- package/dist/{Route53Provider-owQQ4pn6.cjs → Route53Provider-DwgXiLxt.cjs} +1 -1
- package/dist/{Route53Provider-owQQ4pn6.cjs.map → Route53Provider-DwgXiLxt.cjs.map} +1 -1
- package/dist/{Route53Provider-QoPgcXxn.mjs → Route53Provider-tlgBzrRQ.mjs} +1 -1
- package/dist/{Route53Provider-QoPgcXxn.mjs.map → Route53Provider-tlgBzrRQ.mjs.map} +1 -1
- package/dist/{SSMStateProvider-CksOTB8M.mjs → SSMStateProvider-BlrTiV5m.mjs} +1 -1
- package/dist/{SSMStateProvider-CksOTB8M.mjs.map → SSMStateProvider-BlrTiV5m.mjs.map} +1 -1
- package/dist/{SSMStateProvider-CT8tjl9o.cjs → SSMStateProvider-Bt6r8CTL.cjs} +1 -1
- package/dist/{SSMStateProvider-CT8tjl9o.cjs.map → SSMStateProvider-Bt6r8CTL.cjs.map} +1 -1
- package/dist/{backup-provisioner-C4noe75O.cjs → backup-provisioner-B3HBN-2k.cjs} +1 -1
- package/dist/{backup-provisioner-C4noe75O.cjs.map → backup-provisioner-B3HBN-2k.cjs.map} +1 -1
- package/dist/{backup-provisioner-BEXoHTuC.mjs → backup-provisioner-Xunq-p_8.mjs} +1 -1
- package/dist/{backup-provisioner-BEXoHTuC.mjs.map → backup-provisioner-Xunq-p_8.mjs.map} +1 -1
- package/dist/{bundler-c6QvGUBM.mjs → bundler-DkA4qzUD.mjs} +2 -2
- package/dist/{bundler-c6QvGUBM.mjs.map → bundler-DkA4qzUD.mjs.map} +1 -1
- package/dist/{bundler-BG6P249p.cjs → bundler-Dufq3uvI.cjs} +2 -2
- package/dist/{bundler-BG6P249p.cjs.map → bundler-Dufq3uvI.cjs.map} +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/dokploy-api-BPYkG-ZZ.cjs +3 -0
- package/dist/dokploy-api-BiSzWM4v.mjs +3 -0
- package/dist/{dokploy-api-Dvyq-LsM.cjs → dokploy-api-CqOIKHme.cjs} +1 -1
- package/dist/{dokploy-api-Dvyq-LsM.cjs.map → dokploy-api-CqOIKHme.cjs.map} +1 -1
- package/dist/{dokploy-api-BIT0683o.mjs → dokploy-api-sfZFlWuE.mjs} +1 -1
- package/dist/{dokploy-api-BIT0683o.mjs.map → dokploy-api-sfZFlWuE.mjs.map} +1 -1
- package/dist/{encryption-CipsS0KW.cjs → encryption-BVS7Vmzs.cjs} +1 -1
- package/dist/encryption-Bd_m4q3g.mjs +3 -0
- package/dist/{encryption-DSkIlEZ6.mjs → encryption-CO5Q4Bhf.mjs} +1 -1
- package/dist/{encryption-DSkIlEZ6.mjs.map → encryption-CO5Q4Bhf.mjs.map} +1 -1
- package/dist/{encryption-DgKS-Dv9.cjs → encryption-MfXAiFTm.cjs} +1 -1
- package/dist/{encryption-DgKS-Dv9.cjs.map → encryption-MfXAiFTm.cjs.map} +1 -1
- package/dist/{index-BBvGMG_A.d.mts → index-Dx9Kk5bR.d.mts} +2 -2
- package/dist/{index-BBvGMG_A.d.mts.map → index-Dx9Kk5bR.d.mts.map} +1 -1
- package/dist/index.cjs +596 -1314
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +725 -1443
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-DHp49UJV.cjs → openapi-CG1LBk7s.cjs} +1 -1
- package/dist/{openapi-DHp49UJV.cjs.map → openapi-CG1LBk7s.cjs.map} +1 -1
- package/dist/{openapi-DF9GyhyZ.mjs → openapi-CtRVU6RD.mjs} +1 -1
- package/dist/{openapi-DF9GyhyZ.mjs.map → openapi-CtRVU6RD.mjs.map} +1 -1
- package/dist/{openapi-react-query-C4UdILaI.mjs → openapi-react-query-C4iIvpPf.mjs} +1 -1
- package/dist/{openapi-react-query-C4UdILaI.mjs.map → openapi-react-query-C4iIvpPf.mjs.map} +1 -1
- package/dist/{openapi-react-query-DYbBq-WJ.cjs → openapi-react-query-CYWik72n.cjs} +1 -1
- package/dist/{openapi-react-query-DYbBq-WJ.cjs.map → openapi-react-query-CYWik72n.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{reconcile-CYGmqXvq.mjs → reconcile-B2FoJE0a.mjs} +1 -1
- package/dist/{reconcile-CYGmqXvq.mjs.map → reconcile-B2FoJE0a.mjs.map} +1 -1
- package/dist/{reconcile-D93YG7G5.cjs → reconcile-BzqKJ2Rt.cjs} +1 -1
- package/dist/{reconcile-D93YG7G5.cjs.map → reconcile-BzqKJ2Rt.cjs.map} +1 -1
- package/dist/setup-DDXUXzzl.mjs +765 -0
- package/dist/setup-DDXUXzzl.mjs.map +1 -0
- package/dist/setup-DLwhfyt-.cjs +8 -0
- package/dist/setup-Dh-B7YTX.mjs +8 -0
- package/dist/setup-WZ-xqAQO.cjs +849 -0
- package/dist/setup-WZ-xqAQO.cjs.map +1 -0
- package/dist/{sync-COnAugP-.mjs → sync-B5UHXwRM.mjs} +1 -1
- package/dist/sync-BLA94rgC.cjs +4 -0
- package/dist/{sync-D_NowTkZ.mjs → sync-CSooqRQ8.mjs} +1 -1
- package/dist/{sync-D_NowTkZ.mjs.map → sync-CSooqRQ8.mjs.map} +1 -1
- package/dist/{sync-DGXXSk2v.cjs → sync-o_CtEbvR.cjs} +1 -1
- package/dist/{sync-DGXXSk2v.cjs.map → sync-o_CtEbvR.cjs.map} +1 -1
- package/dist/{types-OszPdw9m.d.mts → types-CqfhdmRi.d.mts} +1 -1
- package/dist/{types-OszPdw9m.d.mts.map → types-CqfhdmRi.d.mts.map} +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +4 -4
- package/src/index.ts +4 -0
- package/src/init/versions.ts +5 -5
- package/src/setup/__tests__/auto-setup.spec.ts +147 -0
- package/src/setup/index.ts +46 -6
- package/src/test/index.ts +17 -0
- package/dist/CachedStateProvider-CC0DyO09.mjs +0 -3
- package/dist/dokploy-api-BOx71j6F.cjs +0 -3
- package/dist/dokploy-api-BZGk-lkD.mjs +0 -3
- package/dist/encryption-Db2zvGqM.mjs +0 -3
- package/dist/sync-D1Pa30oV.cjs +0 -4
package/dist/index.cjs
CHANGED
|
@@ -5,12 +5,13 @@ const require_config = require('./config-DujV8DbR.cjs');
|
|
|
5
5
|
const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
6
6
|
const require_fullstack_secrets = require('./fullstack-secrets-BYzi6Hwl.cjs');
|
|
7
7
|
const require_storage = require('./storage-B1wvztiJ.cjs');
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
8
|
+
const require_setup = require('./setup-WZ-xqAQO.cjs');
|
|
9
|
+
const require_openapi = require('./openapi-CG1LBk7s.cjs');
|
|
10
|
+
const require_dokploy_api = require('./dokploy-api-CqOIKHme.cjs');
|
|
11
|
+
const require_encryption = require('./encryption-MfXAiFTm.cjs');
|
|
12
|
+
const require_CachedStateProvider = require('./CachedStateProvider-klLWZXWF.cjs');
|
|
13
|
+
const require_openapi_react_query = require('./openapi-react-query-CYWik72n.cjs');
|
|
14
|
+
require('./sync-o_CtEbvR.cjs');
|
|
14
15
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
15
16
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
16
17
|
const commander = require_chunk.__toESM(require("commander"));
|
|
@@ -20,8 +21,6 @@ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
|
|
|
20
21
|
const node_child_process = require_chunk.__toESM(require("node:child_process"));
|
|
21
22
|
const chokidar = require_chunk.__toESM(require("chokidar"));
|
|
22
23
|
const fast_glob = require_chunk.__toESM(require("fast-glob"));
|
|
23
|
-
const node_net = require_chunk.__toESM(require("node:net"));
|
|
24
|
-
const dotenv = require_chunk.__toESM(require("dotenv"));
|
|
25
24
|
const yaml = require_chunk.__toESM(require("yaml"));
|
|
26
25
|
const node_crypto = require_chunk.__toESM(require("node:crypto"));
|
|
27
26
|
const __geekmidas_constructs_crons = require_chunk.__toESM(require("@geekmidas/constructs/crons"));
|
|
@@ -35,7 +34,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
35
34
|
|
|
36
35
|
//#region package.json
|
|
37
36
|
var name = "@geekmidas/cli";
|
|
38
|
-
var version = "1.10.
|
|
37
|
+
var version = "1.10.41";
|
|
39
38
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
40
39
|
var private$1 = false;
|
|
41
40
|
var type = "module";
|
|
@@ -158,12 +157,12 @@ var package_default = {
|
|
|
158
157
|
|
|
159
158
|
//#endregion
|
|
160
159
|
//#region src/auth/index.ts
|
|
161
|
-
const logger$
|
|
160
|
+
const logger$12 = console;
|
|
162
161
|
/**
|
|
163
162
|
* Validate Dokploy token by making a test API call
|
|
164
163
|
*/
|
|
165
164
|
async function validateDokployToken(endpoint, token) {
|
|
166
|
-
const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-
|
|
165
|
+
const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-BPYkG-ZZ.cjs"));
|
|
167
166
|
const api = new DokployApi$1({
|
|
168
167
|
baseUrl: endpoint,
|
|
169
168
|
token
|
|
@@ -177,7 +176,7 @@ async function prompt$1(message, hidden = false) {
|
|
|
177
176
|
if (!process.stdin.isTTY) throw new Error("Interactive input required. Please provide --token option.");
|
|
178
177
|
if (hidden) {
|
|
179
178
|
process.stdout.write(message);
|
|
180
|
-
return new Promise((resolve$
|
|
179
|
+
return new Promise((resolve$3, reject) => {
|
|
181
180
|
let value = "";
|
|
182
181
|
const cleanup = () => {
|
|
183
182
|
process.stdin.setRawMode(false);
|
|
@@ -194,7 +193,7 @@ async function prompt$1(message, hidden = false) {
|
|
|
194
193
|
if (c === "\n" || c === "\r") {
|
|
195
194
|
cleanup();
|
|
196
195
|
process.stdout.write("\n");
|
|
197
|
-
resolve$
|
|
196
|
+
resolve$3(value);
|
|
198
197
|
} else if (c === "") {
|
|
199
198
|
cleanup();
|
|
200
199
|
process.stdout.write("\n");
|
|
@@ -226,36 +225,36 @@ async function prompt$1(message, hidden = false) {
|
|
|
226
225
|
async function loginCommand(options) {
|
|
227
226
|
const { service, token: providedToken, endpoint: providedEndpoint } = options;
|
|
228
227
|
if (service === "dokploy") {
|
|
229
|
-
logger$
|
|
228
|
+
logger$12.log("\n🔐 Logging in to Dokploy...\n");
|
|
230
229
|
let endpoint = providedEndpoint;
|
|
231
230
|
if (!endpoint) endpoint = await prompt$1("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
232
231
|
endpoint = endpoint.replace(/\/$/, "");
|
|
233
232
|
try {
|
|
234
233
|
new URL(endpoint);
|
|
235
234
|
} catch {
|
|
236
|
-
logger$
|
|
235
|
+
logger$12.error("Invalid URL format");
|
|
237
236
|
process.exit(1);
|
|
238
237
|
}
|
|
239
238
|
let token = providedToken;
|
|
240
239
|
if (!token) {
|
|
241
|
-
logger$
|
|
240
|
+
logger$12.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
|
|
242
241
|
token = await prompt$1("API Token: ", true);
|
|
243
242
|
}
|
|
244
243
|
if (!token) {
|
|
245
|
-
logger$
|
|
244
|
+
logger$12.error("Token is required");
|
|
246
245
|
process.exit(1);
|
|
247
246
|
}
|
|
248
|
-
logger$
|
|
247
|
+
logger$12.log("\nValidating credentials...");
|
|
249
248
|
const isValid = await validateDokployToken(endpoint, token);
|
|
250
249
|
if (!isValid) {
|
|
251
|
-
logger$
|
|
250
|
+
logger$12.error("\n✗ Invalid credentials. Please check your token and try again.");
|
|
252
251
|
process.exit(1);
|
|
253
252
|
}
|
|
254
253
|
await require_credentials.storeDokployCredentials(token, endpoint);
|
|
255
|
-
logger$
|
|
256
|
-
logger$
|
|
257
|
-
logger$
|
|
258
|
-
logger$
|
|
254
|
+
logger$12.log("\n✓ Successfully logged in to Dokploy!");
|
|
255
|
+
logger$12.log(` Endpoint: ${endpoint}`);
|
|
256
|
+
logger$12.log(` Credentials stored in: ${require_credentials.getCredentialsPath()}`);
|
|
257
|
+
logger$12.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
|
|
259
258
|
}
|
|
260
259
|
}
|
|
261
260
|
/**
|
|
@@ -265,28 +264,28 @@ async function logoutCommand(options) {
|
|
|
265
264
|
const { service = "dokploy" } = options;
|
|
266
265
|
if (service === "all") {
|
|
267
266
|
const dokployRemoved = await require_credentials.removeDokployCredentials();
|
|
268
|
-
if (dokployRemoved) logger$
|
|
269
|
-
else logger$
|
|
267
|
+
if (dokployRemoved) logger$12.log("\n✓ Logged out from all services");
|
|
268
|
+
else logger$12.log("\nNo stored credentials found");
|
|
270
269
|
return;
|
|
271
270
|
}
|
|
272
271
|
if (service === "dokploy") {
|
|
273
272
|
const removed = await require_credentials.removeDokployCredentials();
|
|
274
|
-
if (removed) logger$
|
|
275
|
-
else logger$
|
|
273
|
+
if (removed) logger$12.log("\n✓ Logged out from Dokploy");
|
|
274
|
+
else logger$12.log("\nNo Dokploy credentials found");
|
|
276
275
|
}
|
|
277
276
|
}
|
|
278
277
|
/**
|
|
279
278
|
* Show current login status
|
|
280
279
|
*/
|
|
281
280
|
async function whoamiCommand() {
|
|
282
|
-
logger$
|
|
281
|
+
logger$12.log("\n📋 Current credentials:\n");
|
|
283
282
|
const dokploy = await require_credentials.getDokployCredentials();
|
|
284
283
|
if (dokploy) {
|
|
285
|
-
logger$
|
|
286
|
-
logger$
|
|
287
|
-
logger$
|
|
288
|
-
} else logger$
|
|
289
|
-
logger$
|
|
284
|
+
logger$12.log(" Dokploy:");
|
|
285
|
+
logger$12.log(` Endpoint: ${dokploy.endpoint}`);
|
|
286
|
+
logger$12.log(` Token: ${maskToken(dokploy.token)}`);
|
|
287
|
+
} else logger$12.log(" Dokploy: Not logged in");
|
|
288
|
+
logger$12.log(`\n Credentials file: ${require_credentials.getCredentialsPath()}`);
|
|
290
289
|
}
|
|
291
290
|
/**
|
|
292
291
|
* Mask a token for display
|
|
@@ -367,477 +366,12 @@ function isEnabled(config) {
|
|
|
367
366
|
return config.enabled !== false;
|
|
368
367
|
}
|
|
369
368
|
|
|
370
|
-
//#endregion
|
|
371
|
-
//#region src/credentials/index.ts
|
|
372
|
-
const logger$13 = console;
|
|
373
|
-
/**
|
|
374
|
-
* Load environment files
|
|
375
|
-
* @internal Exported for testing
|
|
376
|
-
*/
|
|
377
|
-
function loadEnvFiles(envConfig, cwd = process.cwd()) {
|
|
378
|
-
const loaded = [];
|
|
379
|
-
const missing = [];
|
|
380
|
-
const envFiles = envConfig ? Array.isArray(envConfig) ? envConfig : [envConfig] : [".env"];
|
|
381
|
-
for (const envFile of envFiles) {
|
|
382
|
-
const envPath = (0, node_path.resolve)(cwd, envFile);
|
|
383
|
-
if ((0, node_fs.existsSync)(envPath)) {
|
|
384
|
-
(0, dotenv.config)({
|
|
385
|
-
path: envPath,
|
|
386
|
-
override: true,
|
|
387
|
-
quiet: true
|
|
388
|
-
});
|
|
389
|
-
loaded.push(envFile);
|
|
390
|
-
} else if (envConfig) missing.push(envFile);
|
|
391
|
-
}
|
|
392
|
-
return {
|
|
393
|
-
loaded,
|
|
394
|
-
missing
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Check if a port is available
|
|
399
|
-
* @internal Exported for testing
|
|
400
|
-
*/
|
|
401
|
-
async function isPortAvailable(port) {
|
|
402
|
-
return new Promise((resolve$4) => {
|
|
403
|
-
const server = (0, node_net.createServer)();
|
|
404
|
-
server.once("error", (err) => {
|
|
405
|
-
if (err.code === "EADDRINUSE") resolve$4(false);
|
|
406
|
-
else resolve$4(false);
|
|
407
|
-
});
|
|
408
|
-
server.once("listening", () => {
|
|
409
|
-
server.close();
|
|
410
|
-
resolve$4(true);
|
|
411
|
-
});
|
|
412
|
-
server.listen(port);
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Find an available port starting from the preferred port
|
|
417
|
-
* @internal Exported for testing
|
|
418
|
-
*/
|
|
419
|
-
async function findAvailablePort(preferredPort, maxAttempts = 10) {
|
|
420
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
421
|
-
const port = preferredPort + i;
|
|
422
|
-
if (await isPortAvailable(port)) return port;
|
|
423
|
-
logger$13.log(`⚠️ Port ${port} is in use, trying ${port + 1}...`);
|
|
424
|
-
}
|
|
425
|
-
throw new Error(`Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`);
|
|
426
|
-
}
|
|
427
|
-
const PORT_STATE_PATH = ".gkm/ports.json";
|
|
428
|
-
/**
|
|
429
|
-
* Parse docker-compose.yml and extract all port mappings that use env var interpolation.
|
|
430
|
-
* Entries like `'${POSTGRES_HOST_PORT:-5432}:5432'` are captured.
|
|
431
|
-
* Fixed port mappings like `'5050:80'` are skipped.
|
|
432
|
-
* @internal Exported for testing
|
|
433
|
-
*/
|
|
434
|
-
function parseComposePortMappings(composePath) {
|
|
435
|
-
if (!(0, node_fs.existsSync)(composePath)) return [];
|
|
436
|
-
const content = (0, node_fs.readFileSync)(composePath, "utf-8");
|
|
437
|
-
const compose = (0, yaml.parse)(content);
|
|
438
|
-
if (!compose?.services) return [];
|
|
439
|
-
const results = [];
|
|
440
|
-
for (const [serviceName, serviceConfig] of Object.entries(compose.services)) for (const portMapping of serviceConfig?.ports ?? []) {
|
|
441
|
-
const match = String(portMapping).match(/\$\{(\w+):-(\d+)\}:(\d+)/);
|
|
442
|
-
if (match?.[1] && match[2] && match[3]) results.push({
|
|
443
|
-
service: serviceName,
|
|
444
|
-
envVar: match[1],
|
|
445
|
-
defaultPort: Number(match[2]),
|
|
446
|
-
containerPort: Number(match[3])
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
return results;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Load saved port state from .gkm/ports.json.
|
|
453
|
-
* @internal Exported for testing
|
|
454
|
-
*/
|
|
455
|
-
async function loadPortState(workspaceRoot) {
|
|
456
|
-
try {
|
|
457
|
-
const raw = await (0, node_fs_promises.readFile)((0, node_path.join)(workspaceRoot, PORT_STATE_PATH), "utf-8");
|
|
458
|
-
return JSON.parse(raw);
|
|
459
|
-
} catch {
|
|
460
|
-
return {};
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Save port state to .gkm/ports.json.
|
|
465
|
-
* @internal Exported for testing
|
|
466
|
-
*/
|
|
467
|
-
async function savePortState(workspaceRoot, ports) {
|
|
468
|
-
const dir = (0, node_path.join)(workspaceRoot, ".gkm");
|
|
469
|
-
await (0, node_fs_promises.mkdir)(dir, { recursive: true });
|
|
470
|
-
await (0, node_fs_promises.writeFile)((0, node_path.join)(workspaceRoot, PORT_STATE_PATH), `${JSON.stringify(ports, null, 2)}\n`);
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Check if a project's own Docker container is running and return its host port.
|
|
474
|
-
* Uses `docker compose port` scoped to the project's compose file.
|
|
475
|
-
* @internal Exported for testing
|
|
476
|
-
*/
|
|
477
|
-
function getContainerHostPort(workspaceRoot, service, containerPort) {
|
|
478
|
-
try {
|
|
479
|
-
const result = (0, node_child_process.execSync)(`docker compose port ${service} ${containerPort}`, {
|
|
480
|
-
cwd: workspaceRoot,
|
|
481
|
-
stdio: "pipe"
|
|
482
|
-
}).toString().trim();
|
|
483
|
-
const match = result.match(/:(\d+)$/);
|
|
484
|
-
return match ? Number(match[1]) : null;
|
|
485
|
-
} catch {
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Resolve host ports for Docker services by parsing docker-compose.yml.
|
|
491
|
-
* Priority: running container → saved state → find available port.
|
|
492
|
-
* Persists resolved ports to .gkm/ports.json.
|
|
493
|
-
* @internal Exported for testing
|
|
494
|
-
*/
|
|
495
|
-
async function resolveServicePorts(workspaceRoot) {
|
|
496
|
-
const composePath = (0, node_path.join)(workspaceRoot, "docker-compose.yml");
|
|
497
|
-
const mappings = parseComposePortMappings(composePath);
|
|
498
|
-
if (mappings.length === 0) return {
|
|
499
|
-
dockerEnv: {},
|
|
500
|
-
ports: {},
|
|
501
|
-
mappings: []
|
|
502
|
-
};
|
|
503
|
-
const savedState = await loadPortState(workspaceRoot);
|
|
504
|
-
const dockerEnv = {};
|
|
505
|
-
const ports = {};
|
|
506
|
-
const assignedPorts = /* @__PURE__ */ new Set();
|
|
507
|
-
logger$13.log("\n🔌 Resolving service ports...");
|
|
508
|
-
for (const mapping of mappings) {
|
|
509
|
-
const containerPort = getContainerHostPort(workspaceRoot, mapping.service, mapping.containerPort);
|
|
510
|
-
if (containerPort !== null) {
|
|
511
|
-
ports[mapping.envVar] = containerPort;
|
|
512
|
-
dockerEnv[mapping.envVar] = String(containerPort);
|
|
513
|
-
assignedPorts.add(containerPort);
|
|
514
|
-
logger$13.log(` 🔄 ${mapping.service}:${mapping.containerPort}: reusing existing container on port ${containerPort}`);
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
const savedPort = savedState[mapping.envVar];
|
|
518
|
-
if (savedPort && !assignedPorts.has(savedPort) && await isPortAvailable(savedPort)) {
|
|
519
|
-
ports[mapping.envVar] = savedPort;
|
|
520
|
-
dockerEnv[mapping.envVar] = String(savedPort);
|
|
521
|
-
assignedPorts.add(savedPort);
|
|
522
|
-
logger$13.log(` 💾 ${mapping.service}:${mapping.containerPort}: using saved port ${savedPort}`);
|
|
523
|
-
continue;
|
|
524
|
-
}
|
|
525
|
-
let resolvedPort = await findAvailablePort(mapping.defaultPort);
|
|
526
|
-
while (assignedPorts.has(resolvedPort)) resolvedPort = await findAvailablePort(resolvedPort + 1);
|
|
527
|
-
ports[mapping.envVar] = resolvedPort;
|
|
528
|
-
dockerEnv[mapping.envVar] = String(resolvedPort);
|
|
529
|
-
assignedPorts.add(resolvedPort);
|
|
530
|
-
if (resolvedPort !== mapping.defaultPort) logger$13.log(` ⚡ ${mapping.service}:${mapping.containerPort}: port ${mapping.defaultPort} occupied, using port ${resolvedPort}`);
|
|
531
|
-
else logger$13.log(` ✅ ${mapping.service}:${mapping.containerPort}: using default port ${resolvedPort}`);
|
|
532
|
-
}
|
|
533
|
-
await savePortState(workspaceRoot, ports);
|
|
534
|
-
return {
|
|
535
|
-
dockerEnv,
|
|
536
|
-
ports,
|
|
537
|
-
mappings
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Replace a port in a URL string.
|
|
542
|
-
* Handles both `hostname:port` and `localhost:port` patterns.
|
|
543
|
-
* @internal Exported for testing
|
|
544
|
-
*/
|
|
545
|
-
function replacePortInUrl(url, oldPort, newPort) {
|
|
546
|
-
if (oldPort === newPort) return url;
|
|
547
|
-
let result = url.replace(new RegExp(`:${oldPort}(?=[/?#]|$)`, "g"), `:${newPort}`);
|
|
548
|
-
result = result.replace(new RegExp(`%3A${oldPort}(?=[%/?#&]|$)`, "gi"), `%3A${newPort}`);
|
|
549
|
-
return result;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Rewrite connection URLs and port vars in secrets with resolved ports.
|
|
553
|
-
* Uses the parsed compose mappings to determine which default ports to replace.
|
|
554
|
-
* Pure transform — does not modify secrets on disk.
|
|
555
|
-
* @internal Exported for testing
|
|
556
|
-
*/
|
|
557
|
-
function rewriteUrlsWithPorts(secrets, resolvedPorts) {
|
|
558
|
-
const { ports, mappings } = resolvedPorts;
|
|
559
|
-
const result = { ...secrets };
|
|
560
|
-
const portReplacements = [];
|
|
561
|
-
const serviceNames = /* @__PURE__ */ new Set();
|
|
562
|
-
for (const mapping of mappings) {
|
|
563
|
-
serviceNames.add(mapping.service);
|
|
564
|
-
const resolved = ports[mapping.envVar];
|
|
565
|
-
if (resolved !== void 0) portReplacements.push({
|
|
566
|
-
defaultPort: mapping.defaultPort,
|
|
567
|
-
resolvedPort: resolved
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
for (const [key, value] of Object.entries(result)) {
|
|
571
|
-
if (!key.endsWith("_HOST")) continue;
|
|
572
|
-
if (serviceNames.has(value)) result[key] = "localhost";
|
|
573
|
-
}
|
|
574
|
-
for (const [key, value] of Object.entries(result)) {
|
|
575
|
-
if (!key.endsWith("_PORT")) continue;
|
|
576
|
-
for (const { defaultPort, resolvedPort } of portReplacements) if (value === String(defaultPort)) result[key] = String(resolvedPort);
|
|
577
|
-
}
|
|
578
|
-
for (const [key, value] of Object.entries(result)) {
|
|
579
|
-
if (!key.endsWith("_URL") && !key.endsWith("_ENDPOINT") && !key.endsWith("_CONNECTION_STRING") && key !== "DATABASE_URL") continue;
|
|
580
|
-
let rewritten = value;
|
|
581
|
-
for (const name$1 of serviceNames) rewritten = rewritten.replace(new RegExp(`@${name$1}:`, "g"), "@localhost:");
|
|
582
|
-
for (const { defaultPort, resolvedPort } of portReplacements) rewritten = replacePortInUrl(rewritten, defaultPort, resolvedPort);
|
|
583
|
-
result[key] = rewritten;
|
|
584
|
-
}
|
|
585
|
-
return result;
|
|
586
|
-
}
|
|
587
|
-
/**
|
|
588
|
-
* Build the environment variables to pass to `docker compose up`.
|
|
589
|
-
* Merges process.env, secrets, and port mappings so that Docker Compose
|
|
590
|
-
* can interpolate variables like ${POSTGRES_USER} correctly.
|
|
591
|
-
* @internal Exported for testing
|
|
592
|
-
*/
|
|
593
|
-
function buildDockerComposeEnv(secretsEnv, portEnv) {
|
|
594
|
-
return {
|
|
595
|
-
...process.env,
|
|
596
|
-
...secretsEnv,
|
|
597
|
-
...portEnv
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* Parse all service names from a docker-compose.yml file.
|
|
602
|
-
* @internal Exported for testing
|
|
603
|
-
*/
|
|
604
|
-
function parseComposeServiceNames(composePath) {
|
|
605
|
-
if (!(0, node_fs.existsSync)(composePath)) return [];
|
|
606
|
-
const content = (0, node_fs.readFileSync)(composePath, "utf-8");
|
|
607
|
-
const compose = (0, yaml.parse)(content);
|
|
608
|
-
return Object.keys(compose?.services ?? {});
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Start docker-compose services for a single-app project (no workspace config).
|
|
612
|
-
* Starts all services defined in docker-compose.yml.
|
|
613
|
-
*/
|
|
614
|
-
async function startComposeServices(cwd, portEnv, secretsEnv) {
|
|
615
|
-
const composeFile = (0, node_path.join)(cwd, "docker-compose.yml");
|
|
616
|
-
if (!(0, node_fs.existsSync)(composeFile)) return;
|
|
617
|
-
const servicesToStart = parseComposeServiceNames(composeFile);
|
|
618
|
-
if (servicesToStart.length === 0) return;
|
|
619
|
-
logger$13.log(`🐳 Starting services: ${servicesToStart.join(", ")}`);
|
|
620
|
-
try {
|
|
621
|
-
(0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
|
|
622
|
-
cwd,
|
|
623
|
-
stdio: "inherit",
|
|
624
|
-
env: buildDockerComposeEnv(secretsEnv, portEnv)
|
|
625
|
-
});
|
|
626
|
-
logger$13.log("✅ Services started");
|
|
627
|
-
} catch (error) {
|
|
628
|
-
logger$13.error("❌ Failed to start services:", error.message);
|
|
629
|
-
throw error;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* Start docker-compose services for a workspace.
|
|
634
|
-
* Discovers all services from docker-compose.yml and starts everything
|
|
635
|
-
* except app services (which are managed by turbo).
|
|
636
|
-
* @internal Exported for testing
|
|
637
|
-
*/
|
|
638
|
-
async function startWorkspaceServices(workspace, portEnv, secretsEnv) {
|
|
639
|
-
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
640
|
-
if (!(0, node_fs.existsSync)(composeFile)) return;
|
|
641
|
-
const allServices = parseComposeServiceNames(composeFile);
|
|
642
|
-
const appNames = new Set(Object.keys(workspace.apps));
|
|
643
|
-
const servicesToStart = allServices.filter((name$1) => !appNames.has(name$1));
|
|
644
|
-
if (servicesToStart.length === 0) return;
|
|
645
|
-
logger$13.log(`🐳 Starting services: ${servicesToStart.join(", ")}`);
|
|
646
|
-
try {
|
|
647
|
-
(0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
|
|
648
|
-
cwd: workspace.root,
|
|
649
|
-
stdio: "inherit",
|
|
650
|
-
env: buildDockerComposeEnv(secretsEnv, portEnv)
|
|
651
|
-
});
|
|
652
|
-
logger$13.log("✅ Services started");
|
|
653
|
-
} catch (error) {
|
|
654
|
-
logger$13.error("❌ Failed to start services:", error.message);
|
|
655
|
-
throw error;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Load and flatten secrets for an app from encrypted storage.
|
|
660
|
-
* For workspace app: maps {APP}_DATABASE_URL → DATABASE_URL.
|
|
661
|
-
* @internal Exported for testing
|
|
662
|
-
*/
|
|
663
|
-
async function loadSecretsForApp(secretsRoot, appName, stages = ["dev", "development"]) {
|
|
664
|
-
let secrets = {};
|
|
665
|
-
for (const stage of stages) if (require_storage.secretsExist(stage, secretsRoot)) {
|
|
666
|
-
const stageSecrets = await require_storage.readStageSecrets(stage, secretsRoot);
|
|
667
|
-
if (stageSecrets) {
|
|
668
|
-
logger$13.log(`🔐 Loading secrets from stage: ${stage}`);
|
|
669
|
-
secrets = require_storage.toEmbeddableSecrets(stageSecrets);
|
|
670
|
-
break;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (Object.keys(secrets).length === 0) return {};
|
|
674
|
-
if (!appName) return secrets;
|
|
675
|
-
const prefix = appName.toUpperCase();
|
|
676
|
-
const mapped = { ...secrets };
|
|
677
|
-
const appDbUrl = secrets[`${prefix}_DATABASE_URL`];
|
|
678
|
-
if (appDbUrl) mapped.DATABASE_URL = appDbUrl;
|
|
679
|
-
return mapped;
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Walk up the directory tree to find the root containing .gkm/secrets/.
|
|
683
|
-
* @internal Exported for testing
|
|
684
|
-
*/
|
|
685
|
-
function findSecretsRoot(startDir) {
|
|
686
|
-
let dir = startDir;
|
|
687
|
-
while (dir !== "/") {
|
|
688
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(dir, ".gkm", "secrets"))) return dir;
|
|
689
|
-
const parent = (0, node_path.dirname)(dir);
|
|
690
|
-
if (parent === dir) break;
|
|
691
|
-
dir = parent;
|
|
692
|
-
}
|
|
693
|
-
return startDir;
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Generate the credentials injection code snippet.
|
|
697
|
-
* This is the common logic used by both entry wrapper and exec preload.
|
|
698
|
-
* @internal
|
|
699
|
-
*/
|
|
700
|
-
function generateCredentialsInjection(secretsJsonPath) {
|
|
701
|
-
return `import { existsSync, readFileSync } from 'node:fs';
|
|
702
|
-
|
|
703
|
-
// Inject dev secrets via globalThis and process.env
|
|
704
|
-
// Using globalThis.__gkm_credentials__ avoids CJS/ESM interop issues where
|
|
705
|
-
// Object.assign on the Credentials export only mutates one module copy.
|
|
706
|
-
const secretsPath = '${secretsJsonPath}';
|
|
707
|
-
if (existsSync(secretsPath)) {
|
|
708
|
-
const secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));
|
|
709
|
-
globalThis.__gkm_credentials__ = secrets;
|
|
710
|
-
Object.assign(process.env, secrets);
|
|
711
|
-
}
|
|
712
|
-
`;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Create a preload script that injects secrets into Credentials.
|
|
716
|
-
* Used by `gkm exec` to inject secrets before running any command.
|
|
717
|
-
* @internal Exported for testing
|
|
718
|
-
*/
|
|
719
|
-
async function createCredentialsPreload(preloadPath, secretsJsonPath) {
|
|
720
|
-
const content = `/**
|
|
721
|
-
* Credentials preload generated by 'gkm exec'
|
|
722
|
-
* This file is loaded via NODE_OPTIONS="--import <path>"
|
|
723
|
-
*/
|
|
724
|
-
${generateCredentialsInjection(secretsJsonPath)}`;
|
|
725
|
-
await (0, node_fs_promises.writeFile)(preloadPath, content);
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Create a wrapper script that injects secrets before importing the entry file.
|
|
729
|
-
* @internal Exported for testing
|
|
730
|
-
*/
|
|
731
|
-
async function createEntryWrapper(wrapperPath, entryPath, secretsJsonPath) {
|
|
732
|
-
const credentialsInjection = secretsJsonPath ? `${generateCredentialsInjection(secretsJsonPath)}
|
|
733
|
-
` : "";
|
|
734
|
-
const content = `#!/usr/bin/env node
|
|
735
|
-
/**
|
|
736
|
-
* Entry wrapper generated by 'gkm dev --entry'
|
|
737
|
-
*/
|
|
738
|
-
${credentialsInjection}// Import and run the user's entry file (dynamic import ensures secrets load first)
|
|
739
|
-
await import('${entryPath}');
|
|
740
|
-
`;
|
|
741
|
-
await (0, node_fs_promises.writeFile)(wrapperPath, content);
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Prepare credentials for dev/exec/test modes.
|
|
745
|
-
* Loads workspace config, secrets, resolves Docker ports, rewrites URLs,
|
|
746
|
-
* injects PORT, dependency URLs, and writes credentials JSON.
|
|
747
|
-
*
|
|
748
|
-
* @param options.resolveDockerPorts - How to resolve Docker ports:
|
|
749
|
-
* - `'full'` (default): probe running containers, saved state, then find available ports. Used by dev/test.
|
|
750
|
-
* - `'readonly'`: check running containers and saved state only, never probe for new ports. Used by exec.
|
|
751
|
-
* @param options.stages - Secret stages to try, in order. Default: ['dev', 'development'].
|
|
752
|
-
* @param options.startDocker - Start Docker Compose services after port resolution. Default: false.
|
|
753
|
-
* @param options.secretsFileName - Custom secrets JSON filename. Default: 'dev-secrets-{appName}.json' or 'dev-secrets.json'.
|
|
754
|
-
* @internal Exported for testing
|
|
755
|
-
*/
|
|
756
|
-
async function prepareEntryCredentials(options) {
|
|
757
|
-
const cwd = options.cwd ?? process.cwd();
|
|
758
|
-
const portMode = options.resolveDockerPorts ?? "full";
|
|
759
|
-
let workspaceAppPort;
|
|
760
|
-
let secretsRoot = cwd;
|
|
761
|
-
let appName;
|
|
762
|
-
let appInfo;
|
|
763
|
-
try {
|
|
764
|
-
appInfo = await require_config.loadWorkspaceAppInfo(cwd);
|
|
765
|
-
workspaceAppPort = appInfo.app.port;
|
|
766
|
-
secretsRoot = appInfo.workspaceRoot;
|
|
767
|
-
appName = appInfo.appName;
|
|
768
|
-
} catch {
|
|
769
|
-
secretsRoot = findSecretsRoot(cwd);
|
|
770
|
-
appName = require_config.getAppNameFromCwd(cwd) ?? void 0;
|
|
771
|
-
}
|
|
772
|
-
const resolvedPort = options.explicitPort ?? workspaceAppPort ?? 3e3;
|
|
773
|
-
const credentials = await loadSecretsForApp(secretsRoot, appName, options.stages);
|
|
774
|
-
credentials.PORT = String(resolvedPort);
|
|
775
|
-
const composePath = (0, node_path.join)(secretsRoot, "docker-compose.yml");
|
|
776
|
-
const mappings = parseComposePortMappings(composePath);
|
|
777
|
-
if (mappings.length > 0) {
|
|
778
|
-
let resolvedPorts;
|
|
779
|
-
if (portMode === "full") resolvedPorts = await resolveServicePorts(secretsRoot);
|
|
780
|
-
else {
|
|
781
|
-
const savedPorts = await loadPortState(secretsRoot);
|
|
782
|
-
const ports = {};
|
|
783
|
-
for (const mapping of mappings) {
|
|
784
|
-
const containerPort = getContainerHostPort(secretsRoot, mapping.service, mapping.containerPort);
|
|
785
|
-
if (containerPort !== null) ports[mapping.envVar] = containerPort;
|
|
786
|
-
else {
|
|
787
|
-
const saved = savedPorts[mapping.envVar];
|
|
788
|
-
if (saved !== void 0) ports[mapping.envVar] = saved;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
resolvedPorts = {
|
|
792
|
-
dockerEnv: {},
|
|
793
|
-
ports,
|
|
794
|
-
mappings
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
if (options.startDocker) if (appInfo) await startWorkspaceServices(appInfo.workspace, resolvedPorts.dockerEnv, credentials);
|
|
798
|
-
else await startComposeServices(secretsRoot, resolvedPorts.dockerEnv, credentials);
|
|
799
|
-
if (Object.keys(resolvedPorts.ports).length > 0) {
|
|
800
|
-
const rewritten = rewriteUrlsWithPorts(credentials, resolvedPorts);
|
|
801
|
-
Object.assign(credentials, rewritten);
|
|
802
|
-
logger$13.log(`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
if (appInfo?.appName) {
|
|
806
|
-
const depEnv = require_workspace.getDependencyEnvVars(appInfo.workspace, appInfo.appName);
|
|
807
|
-
Object.assign(credentials, depEnv);
|
|
808
|
-
}
|
|
809
|
-
if (!credentials.EVENT_PUBLISHER_CONNECTION_STRING && !credentials.EVENT_SUBSCRIBER_CONNECTION_STRING && credentials.PGBOSS_DB_USER) {
|
|
810
|
-
const pgbossUrl = require_fullstack_secrets.generatePgBossUrl({
|
|
811
|
-
username: credentials.PGBOSS_DB_USER,
|
|
812
|
-
password: credentials.PGBOSS_DB_PASSWORD ?? "",
|
|
813
|
-
host: credentials.POSTGRES_HOST ?? "localhost",
|
|
814
|
-
port: Number(credentials.POSTGRES_PORT ?? "5432"),
|
|
815
|
-
database: credentials.POSTGRES_DB ?? "app"
|
|
816
|
-
});
|
|
817
|
-
credentials.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;
|
|
818
|
-
credentials.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;
|
|
819
|
-
}
|
|
820
|
-
const secretsDir = (0, node_path.join)(secretsRoot, ".gkm");
|
|
821
|
-
await (0, node_fs_promises.mkdir)(secretsDir, { recursive: true });
|
|
822
|
-
const secretsFileName = options.secretsFileName ?? (appName ? `dev-secrets-${appName}.json` : "dev-secrets.json");
|
|
823
|
-
const secretsJsonPath = (0, node_path.join)(secretsDir, secretsFileName);
|
|
824
|
-
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(credentials, null, 2));
|
|
825
|
-
return {
|
|
826
|
-
credentials,
|
|
827
|
-
resolvedPort,
|
|
828
|
-
secretsJsonPath,
|
|
829
|
-
appName,
|
|
830
|
-
secretsRoot,
|
|
831
|
-
appInfo
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
|
|
835
369
|
//#endregion
|
|
836
370
|
//#region src/generators/CronGenerator.ts
|
|
837
371
|
var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
838
372
|
async build(context, constructs, outputDir, options) {
|
|
839
373
|
const provider = options?.provider || "aws-lambda";
|
|
840
|
-
const logger$
|
|
374
|
+
const logger$13 = console;
|
|
841
375
|
const cronInfos = [];
|
|
842
376
|
if (constructs.length === 0 || provider !== "aws-lambda") return cronInfos;
|
|
843
377
|
const cronsDir = (0, node_path.join)(outputDir, "crons");
|
|
@@ -852,7 +386,7 @@ var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
852
386
|
memorySize: construct.memorySize,
|
|
853
387
|
environment: await construct.getEnvironment({ markOptional: context.markOptional })
|
|
854
388
|
});
|
|
855
|
-
logger$
|
|
389
|
+
logger$13.log(`Generated cron handler: ${key}`);
|
|
856
390
|
}
|
|
857
391
|
return cronInfos;
|
|
858
392
|
}
|
|
@@ -888,7 +422,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
888
422
|
}
|
|
889
423
|
async build(context, constructs, outputDir, options) {
|
|
890
424
|
const provider = options?.provider || "aws-lambda";
|
|
891
|
-
const logger$
|
|
425
|
+
const logger$13 = console;
|
|
892
426
|
const functionInfos = [];
|
|
893
427
|
if (constructs.length === 0 || provider !== "aws-lambda") return functionInfos;
|
|
894
428
|
const functionsDir = (0, node_path.join)(outputDir, "functions");
|
|
@@ -902,7 +436,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
902
436
|
memorySize: construct.memorySize,
|
|
903
437
|
environment: await construct.getEnvironment({ markOptional: context.markOptional })
|
|
904
438
|
});
|
|
905
|
-
logger$
|
|
439
|
+
logger$13.log(`Generated function handler: ${key}`);
|
|
906
440
|
}
|
|
907
441
|
return functionInfos;
|
|
908
442
|
}
|
|
@@ -935,11 +469,11 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
935
469
|
}
|
|
936
470
|
async build(context, constructs, outputDir, options) {
|
|
937
471
|
const provider = options?.provider || "aws-lambda";
|
|
938
|
-
const logger$
|
|
472
|
+
const logger$13 = console;
|
|
939
473
|
const subscriberInfos = [];
|
|
940
474
|
if (provider === "server") {
|
|
941
475
|
await this.generateServerSubscribersFile(outputDir, constructs);
|
|
942
|
-
logger$
|
|
476
|
+
logger$13.log(`Generated server subscribers file with ${constructs.length} subscribers (polling mode)`);
|
|
943
477
|
return subscriberInfos;
|
|
944
478
|
}
|
|
945
479
|
if (constructs.length === 0) return subscriberInfos;
|
|
@@ -956,7 +490,7 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
956
490
|
memorySize: construct.memorySize,
|
|
957
491
|
environment: await construct.getEnvironment({ markOptional: context.markOptional })
|
|
958
492
|
});
|
|
959
|
-
logger$
|
|
493
|
+
logger$13.log(`Generated subscriber handler: ${key}`);
|
|
960
494
|
}
|
|
961
495
|
return subscriberInfos;
|
|
962
496
|
}
|
|
@@ -1122,7 +656,7 @@ export async function setupSubscribers(
|
|
|
1122
656
|
|
|
1123
657
|
//#endregion
|
|
1124
658
|
//#region src/exec/index.ts
|
|
1125
|
-
const logger$
|
|
659
|
+
const logger$11 = console;
|
|
1126
660
|
/**
|
|
1127
661
|
* Run a command with secrets injected into Credentials.
|
|
1128
662
|
* Uses Node's --import flag to preload a script that populates Credentials
|
|
@@ -1137,22 +671,22 @@ const logger$12 = console;
|
|
|
1137
671
|
async function execCommand(commandArgs, options = {}) {
|
|
1138
672
|
const cwd = options.cwd ?? process.cwd();
|
|
1139
673
|
if (commandArgs.length === 0) throw new Error("No command specified. Usage: gkm exec -- <command>");
|
|
1140
|
-
const defaultEnv = loadEnvFiles(".env");
|
|
1141
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1142
|
-
const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({
|
|
674
|
+
const defaultEnv = require_setup.loadEnvFiles(".env");
|
|
675
|
+
if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
676
|
+
const { credentials, secretsJsonPath, appName } = await require_setup.prepareEntryCredentials({
|
|
1143
677
|
cwd,
|
|
1144
678
|
resolveDockerPorts: "readonly"
|
|
1145
679
|
});
|
|
1146
|
-
if (appName) logger$
|
|
680
|
+
if (appName) logger$11.log(`📦 App: ${appName}`);
|
|
1147
681
|
const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
|
|
1148
|
-
if (secretCount > 0) logger$
|
|
682
|
+
if (secretCount > 0) logger$11.log(`🔐 Loaded ${secretCount} secret(s)`);
|
|
1149
683
|
const preloadDir = (0, node_path.join)(cwd, ".gkm");
|
|
1150
684
|
await (0, node_fs_promises.mkdir)(preloadDir, { recursive: true });
|
|
1151
685
|
const preloadPath = (0, node_path.join)(preloadDir, "credentials-preload.mjs");
|
|
1152
|
-
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
686
|
+
await require_setup.createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
1153
687
|
const [cmd, ...args] = commandArgs;
|
|
1154
688
|
if (!cmd) throw new Error("No command specified");
|
|
1155
|
-
logger$
|
|
689
|
+
logger$11.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
|
|
1156
690
|
const existingNodeOptions = (process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim();
|
|
1157
691
|
const preloadImport = `--import=${preloadPath}`;
|
|
1158
692
|
const nodeOptions = [existingNodeOptions, preloadImport].filter(Boolean).join(" ");
|
|
@@ -1165,11 +699,11 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1165
699
|
NODE_OPTIONS: nodeOptions
|
|
1166
700
|
}
|
|
1167
701
|
});
|
|
1168
|
-
const exitCode = await new Promise((resolve$
|
|
1169
|
-
child.on("close", (code) => resolve$
|
|
702
|
+
const exitCode = await new Promise((resolve$3) => {
|
|
703
|
+
child.on("close", (code) => resolve$3(code ?? 0));
|
|
1170
704
|
child.on("error", (error) => {
|
|
1171
|
-
logger$
|
|
1172
|
-
resolve$
|
|
705
|
+
logger$11.error(`Failed to run command: ${error.message}`);
|
|
706
|
+
resolve$3(1);
|
|
1173
707
|
});
|
|
1174
708
|
});
|
|
1175
709
|
if (exitCode !== 0) process.exit(exitCode);
|
|
@@ -1177,7 +711,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1177
711
|
|
|
1178
712
|
//#endregion
|
|
1179
713
|
//#region src/dev/index.ts
|
|
1180
|
-
const logger$
|
|
714
|
+
const logger$10 = console;
|
|
1181
715
|
/**
|
|
1182
716
|
* Normalize telescope configuration
|
|
1183
717
|
* @internal Exported for testing
|
|
@@ -1274,8 +808,8 @@ function getProductionConfigFromGkm(config) {
|
|
|
1274
808
|
}
|
|
1275
809
|
async function devCommand(options) {
|
|
1276
810
|
if (options.entry) return entryDevCommand(options);
|
|
1277
|
-
const defaultEnv = loadEnvFiles(".env");
|
|
1278
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
811
|
+
const defaultEnv = require_setup.loadEnvFiles(".env");
|
|
812
|
+
if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1279
813
|
const appName = require_config.getAppNameFromCwd();
|
|
1280
814
|
let config;
|
|
1281
815
|
let appRoot = process.cwd();
|
|
@@ -1291,9 +825,9 @@ async function devCommand(options) {
|
|
|
1291
825
|
workspaceAppName = appConfig.appName;
|
|
1292
826
|
workspaceAppPort = appConfig.app.port;
|
|
1293
827
|
workspace = appConfig.workspace;
|
|
1294
|
-
logger$
|
|
828
|
+
logger$10.log(`📦 Running app: ${appConfig.appName} on port ${workspaceAppPort}`);
|
|
1295
829
|
if (appConfig.app.entry) {
|
|
1296
|
-
logger$
|
|
830
|
+
logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
|
|
1297
831
|
return entryDevCommand({
|
|
1298
832
|
...options,
|
|
1299
833
|
entry: appConfig.app.entry,
|
|
@@ -1304,7 +838,7 @@ async function devCommand(options) {
|
|
|
1304
838
|
} catch {
|
|
1305
839
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
1306
840
|
if (loadedConfig.type === "workspace") {
|
|
1307
|
-
logger$
|
|
841
|
+
logger$10.log("📦 Detected workspace configuration");
|
|
1308
842
|
return workspaceDevCommand(loadedConfig.workspace, options);
|
|
1309
843
|
}
|
|
1310
844
|
config = loadedConfig.raw;
|
|
@@ -1312,34 +846,34 @@ async function devCommand(options) {
|
|
|
1312
846
|
else {
|
|
1313
847
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
1314
848
|
if (loadedConfig.type === "workspace") {
|
|
1315
|
-
logger$
|
|
849
|
+
logger$10.log("📦 Detected workspace configuration");
|
|
1316
850
|
return workspaceDevCommand(loadedConfig.workspace, options);
|
|
1317
851
|
}
|
|
1318
852
|
config = loadedConfig.raw;
|
|
1319
853
|
}
|
|
1320
854
|
if (config.env) {
|
|
1321
|
-
const { loaded, missing } = loadEnvFiles(config.env, appRoot);
|
|
1322
|
-
if (loaded.length > 0) logger$
|
|
1323
|
-
if (missing.length > 0) logger$
|
|
855
|
+
const { loaded, missing } = require_setup.loadEnvFiles(config.env, appRoot);
|
|
856
|
+
if (loaded.length > 0) logger$10.log(`📦 Loaded env: ${loaded.join(", ")}`);
|
|
857
|
+
if (missing.length > 0) logger$10.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
|
|
1324
858
|
}
|
|
1325
859
|
const resolved = resolveProviders(config, { provider: "server" });
|
|
1326
|
-
logger$
|
|
1327
|
-
logger$
|
|
1328
|
-
if (config.functions) logger$
|
|
1329
|
-
if (config.crons) logger$
|
|
1330
|
-
if (config.subscribers) logger$
|
|
1331
|
-
logger$
|
|
860
|
+
logger$10.log("🚀 Starting development server...");
|
|
861
|
+
logger$10.log(`Loading routes from: ${config.routes}`);
|
|
862
|
+
if (config.functions) logger$10.log(`Loading functions from: ${config.functions}`);
|
|
863
|
+
if (config.crons) logger$10.log(`Loading crons from: ${config.crons}`);
|
|
864
|
+
if (config.subscribers) logger$10.log(`Loading subscribers from: ${config.subscribers}`);
|
|
865
|
+
logger$10.log(`Using envParser: ${config.envParser}`);
|
|
1332
866
|
const { path: envParserPath, importPattern: envParserImportPattern } = require_config.parseModuleConfig(config.envParser, "envParser");
|
|
1333
867
|
const { path: loggerPath, importPattern: loggerImportPattern } = require_config.parseModuleConfig(config.logger, "logger");
|
|
1334
868
|
const telescope = normalizeTelescopeConfig(config.telescope);
|
|
1335
|
-
if (telescope) logger$
|
|
869
|
+
if (telescope) logger$10.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
1336
870
|
const studio = normalizeStudioConfig(config.studio);
|
|
1337
|
-
if (studio) logger$
|
|
871
|
+
if (studio) logger$10.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
1338
872
|
const hooks = normalizeHooksConfig(config.hooks, appRoot);
|
|
1339
|
-
if (hooks) logger$
|
|
873
|
+
if (hooks) logger$10.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);
|
|
1340
874
|
const openApiConfig = require_openapi.resolveOpenApiConfig(config);
|
|
1341
875
|
const enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;
|
|
1342
|
-
if (enableOpenApi) logger$
|
|
876
|
+
if (enableOpenApi) logger$10.log(`📄 OpenAPI output: ${require_openapi.OPENAPI_OUTPUT_PATH}`);
|
|
1343
877
|
const buildContext = {
|
|
1344
878
|
envParserPath,
|
|
1345
879
|
envParserImportPattern,
|
|
@@ -1353,12 +887,12 @@ async function devCommand(options) {
|
|
|
1353
887
|
if (enableOpenApi) await require_openapi.generateOpenApi(config);
|
|
1354
888
|
const runtime = config.runtime ?? "node";
|
|
1355
889
|
let secretsJsonPath;
|
|
1356
|
-
const appSecrets = await loadSecretsForApp(secretsRoot, workspaceAppName);
|
|
1357
|
-
const resolvedPorts = await resolveServicePorts(secretsRoot);
|
|
890
|
+
const appSecrets = await require_setup.loadSecretsForApp(secretsRoot, workspaceAppName);
|
|
891
|
+
const resolvedPorts = await require_setup.resolveServicePorts(secretsRoot);
|
|
1358
892
|
if (Object.keys(resolvedPorts.ports).length > 0) {
|
|
1359
|
-
const rewritten = rewriteUrlsWithPorts(appSecrets, resolvedPorts);
|
|
893
|
+
const rewritten = require_setup.rewriteUrlsWithPorts(appSecrets, resolvedPorts);
|
|
1360
894
|
Object.assign(appSecrets, rewritten);
|
|
1361
|
-
logger$
|
|
895
|
+
logger$10.log(`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
|
|
1362
896
|
}
|
|
1363
897
|
if (workspace && workspaceAppName) {
|
|
1364
898
|
const depEnv = require_workspace.getDependencyEnvVars(workspace, workspaceAppName);
|
|
@@ -1370,7 +904,7 @@ async function devCommand(options) {
|
|
|
1370
904
|
const secretsFileName = workspaceAppName ? `dev-secrets-${workspaceAppName}.json` : "dev-secrets.json";
|
|
1371
905
|
secretsJsonPath = (0, node_path.join)(secretsDir, secretsFileName);
|
|
1372
906
|
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
|
|
1373
|
-
logger$
|
|
907
|
+
logger$10.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
|
|
1374
908
|
}
|
|
1375
909
|
const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath);
|
|
1376
910
|
await devServer.start();
|
|
@@ -1388,7 +922,7 @@ async function devCommand(options) {
|
|
|
1388
922
|
...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []
|
|
1389
923
|
].flat().filter((p) => typeof p === "string");
|
|
1390
924
|
const normalizedPatterns = watchPatterns.map((p) => p.startsWith("./") ? p.slice(2) : p);
|
|
1391
|
-
logger$
|
|
925
|
+
logger$10.log(`👀 Watching for changes in: ${normalizedPatterns.join(", ")}`);
|
|
1392
926
|
const resolvedFiles = await (0, fast_glob.default)(normalizedPatterns, {
|
|
1393
927
|
cwd: appRoot,
|
|
1394
928
|
absolute: false,
|
|
@@ -1398,7 +932,7 @@ async function devCommand(options) {
|
|
|
1398
932
|
const parts = f.split("/");
|
|
1399
933
|
return parts.slice(0, -1).join("/");
|
|
1400
934
|
}))];
|
|
1401
|
-
logger$
|
|
935
|
+
logger$10.log(`📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`);
|
|
1402
936
|
const watcher = chokidar.default.watch([...resolvedFiles, ...dirsToWatch], {
|
|
1403
937
|
ignored: /(^|[/\\])\../,
|
|
1404
938
|
persistent: true,
|
|
@@ -1406,29 +940,29 @@ async function devCommand(options) {
|
|
|
1406
940
|
cwd: appRoot
|
|
1407
941
|
});
|
|
1408
942
|
watcher.on("ready", () => {
|
|
1409
|
-
logger$
|
|
943
|
+
logger$10.log("🔍 File watcher ready");
|
|
1410
944
|
});
|
|
1411
945
|
watcher.on("error", (error) => {
|
|
1412
|
-
logger$
|
|
946
|
+
logger$10.error("❌ Watcher error:", error);
|
|
1413
947
|
});
|
|
1414
948
|
let rebuildTimeout = null;
|
|
1415
949
|
watcher.on("change", async (path) => {
|
|
1416
|
-
logger$
|
|
950
|
+
logger$10.log(`📝 File changed: ${path}`);
|
|
1417
951
|
if (rebuildTimeout) clearTimeout(rebuildTimeout);
|
|
1418
952
|
rebuildTimeout = setTimeout(async () => {
|
|
1419
953
|
try {
|
|
1420
|
-
logger$
|
|
954
|
+
logger$10.log("🔄 Rebuilding...");
|
|
1421
955
|
await buildServer(config, buildContext, resolved.providers[0], enableOpenApi, appRoot, true);
|
|
1422
956
|
if (enableOpenApi) await require_openapi.generateOpenApi(config, {
|
|
1423
957
|
silent: true,
|
|
1424
958
|
bustCache: true
|
|
1425
959
|
});
|
|
1426
|
-
logger$
|
|
960
|
+
logger$10.log("✅ Rebuild complete, restarting server...");
|
|
1427
961
|
await devServer.restart();
|
|
1428
962
|
} catch (error) {
|
|
1429
963
|
const err = error;
|
|
1430
|
-
logger$
|
|
1431
|
-
if (err.stack) logger$
|
|
964
|
+
logger$10.error(`❌ Rebuild failed: ${err.message}`);
|
|
965
|
+
if (err.stack) logger$10.error(err.stack);
|
|
1432
966
|
}
|
|
1433
967
|
}, 300);
|
|
1434
968
|
});
|
|
@@ -1436,9 +970,9 @@ async function devCommand(options) {
|
|
|
1436
970
|
const shutdown = () => {
|
|
1437
971
|
if (isShuttingDown) return;
|
|
1438
972
|
isShuttingDown = true;
|
|
1439
|
-
logger$
|
|
973
|
+
logger$10.log("\n🛑 Shutting down...");
|
|
1440
974
|
Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
|
|
1441
|
-
logger$
|
|
975
|
+
logger$10.error("Error during shutdown:", err);
|
|
1442
976
|
}).finally(() => {
|
|
1443
977
|
process.exit(0);
|
|
1444
978
|
});
|
|
@@ -1637,11 +1171,11 @@ async function loadDevSecrets(workspace) {
|
|
|
1637
1171
|
for (const stage of stages) if (require_storage.secretsExist(stage, workspace.root)) {
|
|
1638
1172
|
const secrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
1639
1173
|
if (secrets) {
|
|
1640
|
-
logger$
|
|
1174
|
+
logger$10.log(`🔐 Loading secrets from stage: ${stage}`);
|
|
1641
1175
|
return require_storage.toEmbeddableSecrets(secrets);
|
|
1642
1176
|
}
|
|
1643
1177
|
}
|
|
1644
|
-
logger$
|
|
1178
|
+
logger$10.warn("⚠️ Secrets enabled but no dev/development secrets found. Run \"gkm setup\" to initialize your development environment");
|
|
1645
1179
|
return {};
|
|
1646
1180
|
}
|
|
1647
1181
|
/**
|
|
@@ -1658,39 +1192,39 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1658
1192
|
const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend");
|
|
1659
1193
|
const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
|
|
1660
1194
|
const mobileApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "mobile");
|
|
1661
|
-
logger$
|
|
1195
|
+
logger$10.log(`\n🚀 Starting workspace: ${workspace.name}`);
|
|
1662
1196
|
const counts = [`${backendApps.length} backend`, `${frontendApps.length} web`];
|
|
1663
1197
|
if (mobileApps.length > 0) counts.push(`${mobileApps.length} mobile`);
|
|
1664
|
-
logger$
|
|
1198
|
+
logger$10.log(` ${counts.join(", ")} app(s)`);
|
|
1665
1199
|
const conflicts = checkPortConflicts(workspace);
|
|
1666
1200
|
if (conflicts.length > 0) {
|
|
1667
|
-
for (const conflict of conflicts) logger$
|
|
1201
|
+
for (const conflict of conflicts) logger$10.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
|
|
1668
1202
|
throw new Error("Port conflicts detected. Please assign unique ports to each app.");
|
|
1669
1203
|
}
|
|
1670
1204
|
if (frontendApps.length > 0) {
|
|
1671
|
-
logger$
|
|
1205
|
+
logger$10.log("\n🔍 Validating frontend apps...");
|
|
1672
1206
|
const validationResults = await validateFrontendApps(workspace);
|
|
1673
1207
|
let hasErrors = false;
|
|
1674
1208
|
for (const result of validationResults) {
|
|
1675
1209
|
if (!result.valid) {
|
|
1676
1210
|
hasErrors = true;
|
|
1677
|
-
logger$
|
|
1678
|
-
for (const error of result.errors) logger$
|
|
1211
|
+
logger$10.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
|
|
1212
|
+
for (const error of result.errors) logger$10.error(` • ${error}`);
|
|
1679
1213
|
}
|
|
1680
|
-
for (const warning of result.warnings) logger$
|
|
1214
|
+
for (const warning of result.warnings) logger$10.warn(` ⚠️ ${result.appName}: ${warning}`);
|
|
1681
1215
|
}
|
|
1682
1216
|
if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
|
|
1683
|
-
logger$
|
|
1217
|
+
logger$10.log("✅ Frontend apps validated");
|
|
1684
1218
|
}
|
|
1685
|
-
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
1219
|
+
const resolvedPorts = await require_setup.resolveServicePorts(workspace.root);
|
|
1686
1220
|
const rawSecrets = await loadDevSecrets(workspace);
|
|
1687
|
-
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
|
|
1688
|
-
const secretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
|
|
1689
|
-
if (Object.keys(secretsEnv).length > 0) logger$
|
|
1221
|
+
await require_setup.startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
|
|
1222
|
+
const secretsEnv = require_setup.rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
|
|
1223
|
+
if (Object.keys(secretsEnv).length > 0) logger$10.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
|
|
1690
1224
|
const dependencyEnv = generateAllDependencyEnvVars(workspace);
|
|
1691
1225
|
if (Object.keys(dependencyEnv).length > 0) {
|
|
1692
|
-
logger$
|
|
1693
|
-
for (const [key, value] of Object.entries(dependencyEnv)) logger$
|
|
1226
|
+
logger$10.log("📡 Dependency URLs:");
|
|
1227
|
+
for (const [key, value] of Object.entries(dependencyEnv)) logger$10.log(` ${key}=${value}`);
|
|
1694
1228
|
}
|
|
1695
1229
|
let turboFilter = [];
|
|
1696
1230
|
if (options.app) {
|
|
@@ -1699,19 +1233,19 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1699
1233
|
throw new Error(`App "${options.app}" not found. Available apps: ${appNames}`);
|
|
1700
1234
|
}
|
|
1701
1235
|
turboFilter = ["--filter", options.app];
|
|
1702
|
-
logger$
|
|
1236
|
+
logger$10.log(`\n🎯 Running single app: ${options.app}`);
|
|
1703
1237
|
} else if (options.filter) {
|
|
1704
1238
|
turboFilter = ["--filter", options.filter];
|
|
1705
|
-
logger$
|
|
1706
|
-
} else logger$
|
|
1239
|
+
logger$10.log(`\n🔍 Using filter: ${options.filter}`);
|
|
1240
|
+
} else logger$10.log(`\n🎯 Running all ${appCount} apps`);
|
|
1707
1241
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
1708
|
-
logger$
|
|
1242
|
+
logger$10.log("\n📋 Apps (in dependency order):");
|
|
1709
1243
|
for (const appName of buildOrder) {
|
|
1710
1244
|
const app = workspace.apps[appName];
|
|
1711
1245
|
if (!app) continue;
|
|
1712
1246
|
const deps = app.dependencies.length > 0 ? ` (depends on: ${app.dependencies.join(", ")})` : "";
|
|
1713
1247
|
const icon = app.type === "backend" ? "🔧" : app.type === "mobile" ? "📱" : "🌐";
|
|
1714
|
-
logger$
|
|
1248
|
+
logger$10.log(` ${icon} ${appName} → http://localhost:${app.port}${deps}`);
|
|
1715
1249
|
}
|
|
1716
1250
|
const configFiles = [
|
|
1717
1251
|
"gkm.config.ts",
|
|
@@ -1733,7 +1267,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1733
1267
|
NODE_ENV: "development",
|
|
1734
1268
|
...configPath ? { GKM_CONFIG_PATH: configPath } : {}
|
|
1735
1269
|
};
|
|
1736
|
-
logger$
|
|
1270
|
+
logger$10.log("\n🏃 Starting turbo run dev...\n");
|
|
1737
1271
|
const turboProcess = (0, node_child_process.spawn)("pnpm", [
|
|
1738
1272
|
"turbo",
|
|
1739
1273
|
"run",
|
|
@@ -1749,7 +1283,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1749
1283
|
const shutdown = () => {
|
|
1750
1284
|
if (isShuttingDown) return;
|
|
1751
1285
|
isShuttingDown = true;
|
|
1752
|
-
logger$
|
|
1286
|
+
logger$10.log("\n🛑 Shutting down workspace...");
|
|
1753
1287
|
const pid = turboProcess.pid;
|
|
1754
1288
|
if (pid) try {
|
|
1755
1289
|
process.kill(-pid, "SIGTERM");
|
|
@@ -1771,14 +1305,14 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1771
1305
|
};
|
|
1772
1306
|
process.on("SIGINT", shutdown);
|
|
1773
1307
|
process.on("SIGTERM", shutdown);
|
|
1774
|
-
return new Promise((resolve$
|
|
1308
|
+
return new Promise((resolve$3, reject) => {
|
|
1775
1309
|
turboProcess.on("error", (error) => {
|
|
1776
|
-
logger$
|
|
1310
|
+
logger$10.error("❌ Turbo error:", error);
|
|
1777
1311
|
reject(error);
|
|
1778
1312
|
});
|
|
1779
1313
|
turboProcess.on("exit", (code) => {
|
|
1780
1314
|
if (code !== null && code !== 0) reject(new Error(`Turbo exited with code ${code}`));
|
|
1781
|
-
else resolve$
|
|
1315
|
+
else resolve$3();
|
|
1782
1316
|
});
|
|
1783
1317
|
});
|
|
1784
1318
|
}
|
|
@@ -1814,23 +1348,23 @@ async function entryDevCommand(options) {
|
|
|
1814
1348
|
if (!entry) throw new Error("--entry requires a file path");
|
|
1815
1349
|
const entryPath = (0, node_path.resolve)(process.cwd(), entry);
|
|
1816
1350
|
if (!(0, node_fs.existsSync)(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
|
|
1817
|
-
const defaultEnv = loadEnvFiles(".env");
|
|
1818
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1819
|
-
const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
|
|
1820
|
-
if (appName) logger$
|
|
1821
|
-
logger$
|
|
1822
|
-
if (Object.keys(credentials).length > 1) logger$
|
|
1351
|
+
const defaultEnv = require_setup.loadEnvFiles(".env");
|
|
1352
|
+
if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1353
|
+
const { credentials, resolvedPort, secretsJsonPath, appName } = await require_setup.prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
|
|
1354
|
+
if (appName) logger$10.log(`📦 App: ${appName} (port ${resolvedPort})`);
|
|
1355
|
+
logger$10.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
|
|
1356
|
+
if (Object.keys(credentials).length > 1) logger$10.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
|
|
1823
1357
|
const wrapperDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
1824
1358
|
await (0, node_fs_promises.mkdir)(wrapperDir, { recursive: true });
|
|
1825
1359
|
const wrapperPath = (0, node_path.join)(wrapperDir, "entry-wrapper.ts");
|
|
1826
|
-
await createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
|
|
1360
|
+
await require_setup.createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
|
|
1827
1361
|
const runner = new EntryRunner(wrapperPath, entryPath, watch, resolvedPort);
|
|
1828
1362
|
await runner.start();
|
|
1829
1363
|
let isShuttingDown = false;
|
|
1830
1364
|
const shutdown = () => {
|
|
1831
1365
|
if (isShuttingDown) return;
|
|
1832
1366
|
isShuttingDown = true;
|
|
1833
|
-
logger$
|
|
1367
|
+
logger$10.log("\n🛑 Shutting down...");
|
|
1834
1368
|
runner.stop();
|
|
1835
1369
|
process.exit(0);
|
|
1836
1370
|
};
|
|
@@ -1862,14 +1396,14 @@ var EntryRunner = class {
|
|
|
1862
1396
|
});
|
|
1863
1397
|
let restartTimeout = null;
|
|
1864
1398
|
this.watcher.on("change", (path) => {
|
|
1865
|
-
logger$
|
|
1399
|
+
logger$10.log(`📝 File changed: ${path}`);
|
|
1866
1400
|
if (restartTimeout) clearTimeout(restartTimeout);
|
|
1867
1401
|
restartTimeout = setTimeout(async () => {
|
|
1868
|
-
logger$
|
|
1402
|
+
logger$10.log("🔄 Restarting...");
|
|
1869
1403
|
await this.restart();
|
|
1870
1404
|
}, 300);
|
|
1871
1405
|
});
|
|
1872
|
-
logger$
|
|
1406
|
+
logger$10.log(`👀 Watching for changes in: ${watchDir}`);
|
|
1873
1407
|
}
|
|
1874
1408
|
}
|
|
1875
1409
|
async runProcess() {
|
|
@@ -1884,22 +1418,22 @@ var EntryRunner = class {
|
|
|
1884
1418
|
});
|
|
1885
1419
|
this.isRunning = true;
|
|
1886
1420
|
this.childProcess.on("error", (error) => {
|
|
1887
|
-
logger$
|
|
1421
|
+
logger$10.error("❌ Process error:", error);
|
|
1888
1422
|
});
|
|
1889
1423
|
this.childProcess.on("exit", (code) => {
|
|
1890
|
-
if (code !== null && code !== 0 && code !== 143) logger$
|
|
1424
|
+
if (code !== null && code !== 0 && code !== 143) logger$10.error(`❌ Process exited with code ${code}`);
|
|
1891
1425
|
this.isRunning = false;
|
|
1892
1426
|
});
|
|
1893
|
-
await new Promise((resolve$
|
|
1427
|
+
await new Promise((resolve$3) => setTimeout(resolve$3, 500));
|
|
1894
1428
|
if (this.isRunning) {
|
|
1895
|
-
logger$
|
|
1896
|
-
logger$
|
|
1897
|
-
logger$
|
|
1429
|
+
logger$10.log("");
|
|
1430
|
+
logger$10.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
|
|
1431
|
+
logger$10.log("");
|
|
1898
1432
|
}
|
|
1899
1433
|
}
|
|
1900
1434
|
async restart() {
|
|
1901
1435
|
this.stopProcess();
|
|
1902
|
-
await new Promise((resolve$
|
|
1436
|
+
await new Promise((resolve$3) => setTimeout(resolve$3, 500));
|
|
1903
1437
|
await this.runProcess();
|
|
1904
1438
|
}
|
|
1905
1439
|
stop() {
|
|
@@ -2003,16 +1537,16 @@ var DevServer = class {
|
|
|
2003
1537
|
this.startTime = Date.now();
|
|
2004
1538
|
if (this.isRunning) await this.stop();
|
|
2005
1539
|
if (this.portExplicit) {
|
|
2006
|
-
const available = await isPortAvailable(this.requestedPort);
|
|
1540
|
+
const available = await require_setup.isPortAvailable(this.requestedPort);
|
|
2007
1541
|
if (!available) throw new Error(`Port ${this.requestedPort} is already in use. Either stop the process using that port or omit -p/--port to auto-select an available port.`);
|
|
2008
1542
|
this.actualPort = this.requestedPort;
|
|
2009
1543
|
} else {
|
|
2010
|
-
this.actualPort = await findAvailablePort(this.requestedPort);
|
|
2011
|
-
if (this.actualPort !== this.requestedPort) logger$
|
|
1544
|
+
this.actualPort = await require_setup.findAvailablePort(this.requestedPort);
|
|
1545
|
+
if (this.actualPort !== this.requestedPort) logger$10.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
|
|
2012
1546
|
}
|
|
2013
1547
|
const serverEntryPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
2014
1548
|
await this.createServerEntry();
|
|
2015
|
-
logger$
|
|
1549
|
+
logger$10.log(`\n⏳ Starting server...`);
|
|
2016
1550
|
this.serverProcess = (0, node_child_process.spawn)("npx", [
|
|
2017
1551
|
"tsx",
|
|
2018
1552
|
serverEntryPath,
|
|
@@ -2028,13 +1562,13 @@ var DevServer = class {
|
|
|
2028
1562
|
});
|
|
2029
1563
|
this.isRunning = true;
|
|
2030
1564
|
this.serverProcess.on("error", (error) => {
|
|
2031
|
-
logger$
|
|
1565
|
+
logger$10.error("❌ Server error:", error);
|
|
2032
1566
|
});
|
|
2033
1567
|
this.serverProcess.on("exit", (code, signal) => {
|
|
2034
|
-
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$
|
|
1568
|
+
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$10.error(`❌ Server exited with code ${code}`);
|
|
2035
1569
|
this.isRunning = false;
|
|
2036
1570
|
});
|
|
2037
|
-
await new Promise((resolve$
|
|
1571
|
+
await new Promise((resolve$3) => setTimeout(resolve$3, 1e3));
|
|
2038
1572
|
if (this.isRunning) {
|
|
2039
1573
|
const base = `http://localhost:${this.actualPort}`;
|
|
2040
1574
|
const lines = [` Local: ${base}`];
|
|
@@ -2044,13 +1578,13 @@ var DevServer = class {
|
|
|
2044
1578
|
const maxLen = Math.max(...lines.map((l) => l.length));
|
|
2045
1579
|
const pad = (s) => s.padEnd(maxLen);
|
|
2046
1580
|
const border = "─".repeat(maxLen + 2);
|
|
2047
|
-
logger$
|
|
2048
|
-
logger$
|
|
2049
|
-
logger$
|
|
2050
|
-
logger$
|
|
2051
|
-
for (const line of lines) logger$
|
|
2052
|
-
logger$
|
|
2053
|
-
logger$
|
|
1581
|
+
logger$10.log("");
|
|
1582
|
+
logger$10.log(` \x1b[32m✓ Ready\x1b[0m in ${((Date.now() - this.startTime) / 1e3).toFixed(1)}s`);
|
|
1583
|
+
logger$10.log("");
|
|
1584
|
+
logger$10.log(` ┌${border}┐`);
|
|
1585
|
+
for (const line of lines) logger$10.log(` │ ${pad(line)} │`);
|
|
1586
|
+
logger$10.log(` └${border}┘`);
|
|
1587
|
+
logger$10.log("");
|
|
2054
1588
|
}
|
|
2055
1589
|
}
|
|
2056
1590
|
async stop() {
|
|
@@ -2079,8 +1613,8 @@ var DevServer = class {
|
|
|
2079
1613
|
await this.stop();
|
|
2080
1614
|
let attempts = 0;
|
|
2081
1615
|
while (attempts < 30) {
|
|
2082
|
-
if (await isPortAvailable(portToReuse)) break;
|
|
2083
|
-
await new Promise((resolve$
|
|
1616
|
+
if (await require_setup.isPortAvailable(portToReuse)) break;
|
|
1617
|
+
await new Promise((resolve$3) => setTimeout(resolve$3, 100));
|
|
2084
1618
|
attempts++;
|
|
2085
1619
|
}
|
|
2086
1620
|
this.requestedPort = portToReuse;
|
|
@@ -2100,7 +1634,7 @@ var DevServer = class {
|
|
|
2100
1634
|
|
|
2101
1635
|
//#endregion
|
|
2102
1636
|
//#region src/build/manifests.ts
|
|
2103
|
-
const logger$
|
|
1637
|
+
const logger$9 = console;
|
|
2104
1638
|
function isPartitioned(field) {
|
|
2105
1639
|
return !Array.isArray(field);
|
|
2106
1640
|
}
|
|
@@ -2161,8 +1695,8 @@ export type RoutePath = Route['path'];
|
|
|
2161
1695
|
`;
|
|
2162
1696
|
const manifestPath = (0, node_path.join)(manifestDir, "aws.ts");
|
|
2163
1697
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
2164
|
-
logger$
|
|
2165
|
-
logger$
|
|
1698
|
+
logger$9.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers`);
|
|
1699
|
+
logger$9.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
2166
1700
|
}
|
|
2167
1701
|
async function generateServerManifest(outputDir, appInfo, routes, subscribers) {
|
|
2168
1702
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
@@ -2188,8 +1722,8 @@ export type RoutePath = Route['path'];
|
|
|
2188
1722
|
`;
|
|
2189
1723
|
const manifestPath = (0, node_path.join)(manifestDir, "server.ts");
|
|
2190
1724
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
2191
|
-
logger$
|
|
2192
|
-
logger$
|
|
1725
|
+
logger$9.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers`);
|
|
1726
|
+
logger$9.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
2193
1727
|
}
|
|
2194
1728
|
/**
|
|
2195
1729
|
* Filter out 'ALL' method routes from a manifest field (flat or partitioned).
|
|
@@ -2255,7 +1789,7 @@ function groupInfosByPartition(infos, constructs) {
|
|
|
2255
1789
|
|
|
2256
1790
|
//#endregion
|
|
2257
1791
|
//#region src/build/index.ts
|
|
2258
|
-
const logger$
|
|
1792
|
+
const logger$8 = console;
|
|
2259
1793
|
async function buildCommand(options) {
|
|
2260
1794
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
2261
1795
|
if (loadedConfig.type === "workspace") {
|
|
@@ -2263,7 +1797,7 @@ async function buildCommand(options) {
|
|
|
2263
1797
|
const workspaceRoot = (0, node_path.resolve)(loadedConfig.workspace.root);
|
|
2264
1798
|
const isAtWorkspaceRoot = cwd === workspaceRoot;
|
|
2265
1799
|
if (isAtWorkspaceRoot) {
|
|
2266
|
-
logger$
|
|
1800
|
+
logger$8.log("📦 Detected workspace configuration");
|
|
2267
1801
|
return workspaceBuildCommand(loadedConfig.workspace, options);
|
|
2268
1802
|
}
|
|
2269
1803
|
}
|
|
@@ -2271,21 +1805,21 @@ async function buildCommand(options) {
|
|
|
2271
1805
|
const resolved = resolveProviders(config, options);
|
|
2272
1806
|
const productionConfigFromGkm = getProductionConfigFromGkm(config);
|
|
2273
1807
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2274
|
-
if (production) logger$
|
|
2275
|
-
logger$
|
|
2276
|
-
logger$
|
|
2277
|
-
if (config.functions) logger$
|
|
2278
|
-
if (config.crons) logger$
|
|
2279
|
-
if (config.subscribers) logger$
|
|
2280
|
-
logger$
|
|
1808
|
+
if (production) logger$8.log(`🏭 Building for PRODUCTION`);
|
|
1809
|
+
logger$8.log(`Building with providers: ${resolved.providers.join(", ")}`);
|
|
1810
|
+
logger$8.log(`Loading routes from: ${formatRoutes(config.routes)}`);
|
|
1811
|
+
if (config.functions) logger$8.log(`Loading functions from: ${formatRoutes(config.functions)}`);
|
|
1812
|
+
if (config.crons) logger$8.log(`Loading crons from: ${formatRoutes(config.crons)}`);
|
|
1813
|
+
if (config.subscribers) logger$8.log(`Loading subscribers from: ${formatRoutes(config.subscribers)}`);
|
|
1814
|
+
logger$8.log(`Using envParser: ${config.envParser}`);
|
|
2281
1815
|
const { path: envParserPath, importPattern: envParserImportPattern } = require_config.parseModuleConfig(config.envParser, "envParser");
|
|
2282
1816
|
const { path: loggerPath, importPattern: loggerImportPattern } = require_config.parseModuleConfig(config.logger, "logger");
|
|
2283
1817
|
const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
|
|
2284
|
-
if (telescope) logger$
|
|
1818
|
+
if (telescope) logger$8.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
2285
1819
|
const studio = production ? void 0 : normalizeStudioConfig(config.studio);
|
|
2286
|
-
if (studio) logger$
|
|
1820
|
+
if (studio) logger$8.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
2287
1821
|
const hooks = normalizeHooksConfig(config.hooks);
|
|
2288
|
-
if (hooks) logger$
|
|
1822
|
+
if (hooks) logger$8.log(`🪝 Server hooks enabled`);
|
|
2289
1823
|
const services = config.docker?.compose?.services;
|
|
2290
1824
|
const dockerServices = services ? Array.isArray(services) ? {
|
|
2291
1825
|
postgres: services.includes("postgres"),
|
|
@@ -2318,12 +1852,12 @@ async function buildCommand(options) {
|
|
|
2318
1852
|
config.crons ? cronGenerator.load(config.crons) : [],
|
|
2319
1853
|
config.subscribers ? subscriberGenerator.load(config.subscribers) : []
|
|
2320
1854
|
]);
|
|
2321
|
-
logger$
|
|
2322
|
-
logger$
|
|
2323
|
-
logger$
|
|
2324
|
-
logger$
|
|
1855
|
+
logger$8.log(`Found ${allEndpoints.length} endpoints`);
|
|
1856
|
+
logger$8.log(`Found ${allFunctions.length} functions`);
|
|
1857
|
+
logger$8.log(`Found ${allCrons.length} crons`);
|
|
1858
|
+
logger$8.log(`Found ${allSubscribers.length} subscribers`);
|
|
2325
1859
|
if (allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0) {
|
|
2326
|
-
logger$
|
|
1860
|
+
logger$8.log("No endpoints, functions, crons, or subscribers found to process");
|
|
2327
1861
|
return {};
|
|
2328
1862
|
}
|
|
2329
1863
|
const rootOutputDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
@@ -2339,7 +1873,7 @@ async function buildCommand(options) {
|
|
|
2339
1873
|
async function buildForProvider(provider, context, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, endpoints, functions, crons, subscribers, enableOpenApi, skipBundle, stage) {
|
|
2340
1874
|
const outputDir = (0, node_path.join)(process.cwd(), ".gkm", provider);
|
|
2341
1875
|
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
2342
|
-
logger$
|
|
1876
|
+
logger$8.log(`\nGenerating handlers for provider: ${provider}`);
|
|
2343
1877
|
const [routes, functionInfos, cronInfos, subscriberInfos] = await Promise.all([
|
|
2344
1878
|
endpointGenerator.build(context, endpoints, outputDir, {
|
|
2345
1879
|
provider,
|
|
@@ -2349,7 +1883,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2349
1883
|
cronGenerator.build(context, crons, outputDir, { provider }),
|
|
2350
1884
|
subscriberGenerator.build(context, subscribers, outputDir, { provider })
|
|
2351
1885
|
]);
|
|
2352
|
-
logger$
|
|
1886
|
+
logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`);
|
|
2353
1887
|
const manifestRoutes = assembleManifestField(routes, endpoints);
|
|
2354
1888
|
const manifestFunctions = assembleManifestField(functionInfos, functions);
|
|
2355
1889
|
const manifestCrons = assembleManifestField(cronInfos, crons);
|
|
@@ -2369,8 +1903,8 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2369
1903
|
await generateServerManifest(rootOutputDir, appInfo, serverRouteField, manifestSubscribers);
|
|
2370
1904
|
let masterKey;
|
|
2371
1905
|
if (context.production?.bundle && !skipBundle) {
|
|
2372
|
-
logger$
|
|
2373
|
-
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-
|
|
1906
|
+
logger$8.log(`\n📦 Bundling production server...`);
|
|
1907
|
+
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-Dufq3uvI.cjs"));
|
|
2374
1908
|
const allConstructs = [
|
|
2375
1909
|
...endpoints.map((e) => e.construct),
|
|
2376
1910
|
...functions.map((f) => f.construct),
|
|
@@ -2389,10 +1923,10 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2389
1923
|
dockerServices
|
|
2390
1924
|
});
|
|
2391
1925
|
masterKey = bundleResult.masterKey;
|
|
2392
|
-
logger$
|
|
1926
|
+
logger$8.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
|
|
2393
1927
|
if (masterKey) {
|
|
2394
|
-
logger$
|
|
2395
|
-
logger$
|
|
1928
|
+
logger$8.log(`\n🔐 Secrets encrypted for deployment`);
|
|
1929
|
+
logger$8.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
|
|
2396
1930
|
}
|
|
2397
1931
|
}
|
|
2398
1932
|
return { masterKey };
|
|
@@ -2429,18 +1963,18 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2429
1963
|
const apps = Object.entries(workspace.apps);
|
|
2430
1964
|
const backendApps = apps.filter(([, app]) => app.type === "backend");
|
|
2431
1965
|
const frontendApps = apps.filter(([, app]) => app.type === "web");
|
|
2432
|
-
logger$
|
|
2433
|
-
logger$
|
|
2434
|
-
logger$
|
|
2435
|
-
if (options.production) logger$
|
|
1966
|
+
logger$8.log(`\n🏗️ Building workspace: ${workspace.name}`);
|
|
1967
|
+
logger$8.log(` Backend apps: ${backendApps.map(([name$1]) => name$1).join(", ") || "none"}`);
|
|
1968
|
+
logger$8.log(` Frontend apps: ${frontendApps.map(([name$1]) => name$1).join(", ") || "none"}`);
|
|
1969
|
+
if (options.production) logger$8.log(` 🏭 Production mode enabled`);
|
|
2436
1970
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
2437
|
-
logger$
|
|
1971
|
+
logger$8.log(` Build order: ${buildOrder.join(" → ")}`);
|
|
2438
1972
|
const pm = detectPackageManager$2();
|
|
2439
|
-
logger$
|
|
1973
|
+
logger$8.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
|
|
2440
1974
|
try {
|
|
2441
1975
|
const turboCommand = getTurboCommand(pm);
|
|
2442
|
-
logger$
|
|
2443
|
-
await new Promise((resolve$
|
|
1976
|
+
logger$8.log(`Running: ${turboCommand}`);
|
|
1977
|
+
await new Promise((resolve$3, reject) => {
|
|
2444
1978
|
const child = (0, node_child_process.spawn)(turboCommand, {
|
|
2445
1979
|
shell: true,
|
|
2446
1980
|
cwd: workspace.root,
|
|
@@ -2451,7 +1985,7 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2451
1985
|
}
|
|
2452
1986
|
});
|
|
2453
1987
|
child.on("close", (code) => {
|
|
2454
|
-
if (code === 0) resolve$
|
|
1988
|
+
if (code === 0) resolve$3();
|
|
2455
1989
|
else reject(new Error(`Turbo build failed with exit code ${code}`));
|
|
2456
1990
|
});
|
|
2457
1991
|
child.on("error", (err) => {
|
|
@@ -2467,16 +2001,16 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2467
2001
|
outputPath
|
|
2468
2002
|
});
|
|
2469
2003
|
}
|
|
2470
|
-
logger$
|
|
2004
|
+
logger$8.log(`\n✅ Workspace build complete!`);
|
|
2471
2005
|
await require_openapi.openapiCommand({ cwd: workspace.root });
|
|
2472
|
-
logger$
|
|
2006
|
+
logger$8.log(`\n📋 Build Summary:`);
|
|
2473
2007
|
for (const result of results) {
|
|
2474
2008
|
const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
|
|
2475
|
-
logger$
|
|
2009
|
+
logger$8.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
|
|
2476
2010
|
}
|
|
2477
2011
|
} catch (error) {
|
|
2478
2012
|
const errorMessage = error instanceof Error ? error.message : "Build failed";
|
|
2479
|
-
logger$
|
|
2013
|
+
logger$8.log(`\n❌ Build failed: ${errorMessage}`);
|
|
2480
2014
|
for (const [appName, app] of apps) results.push({
|
|
2481
2015
|
appName,
|
|
2482
2016
|
type: app.type,
|
|
@@ -2703,11 +2237,11 @@ async function createDnsProvider(options) {
|
|
|
2703
2237
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
2704
2238
|
const provider = config.provider;
|
|
2705
2239
|
if (provider === "hostinger") {
|
|
2706
|
-
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-
|
|
2240
|
+
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-CrHfsgz9.cjs"));
|
|
2707
2241
|
return new HostingerProvider();
|
|
2708
2242
|
}
|
|
2709
2243
|
if (provider === "route53") {
|
|
2710
|
-
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-
|
|
2244
|
+
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-DwgXiLxt.cjs"));
|
|
2711
2245
|
const route53Config = config;
|
|
2712
2246
|
return new Route53Provider({
|
|
2713
2247
|
region: route53Config.region,
|
|
@@ -2721,7 +2255,7 @@ async function createDnsProvider(options) {
|
|
|
2721
2255
|
|
|
2722
2256
|
//#endregion
|
|
2723
2257
|
//#region src/deploy/dns/index.ts
|
|
2724
|
-
const logger$
|
|
2258
|
+
const logger$7 = console;
|
|
2725
2259
|
/**
|
|
2726
2260
|
* Check if DNS config is legacy format (single domain with `domain` property)
|
|
2727
2261
|
*/
|
|
@@ -2758,7 +2292,7 @@ function groupHostnamesByDomain(appHostnames, dnsConfig) {
|
|
|
2758
2292
|
for (const [appName, hostname] of appHostnames) {
|
|
2759
2293
|
const rootDomain = findRootDomain(hostname, dnsConfig);
|
|
2760
2294
|
if (!rootDomain) {
|
|
2761
|
-
logger$
|
|
2295
|
+
logger$7.log(` ⚠ No DNS config found for hostname: ${hostname}`);
|
|
2762
2296
|
continue;
|
|
2763
2297
|
}
|
|
2764
2298
|
if (!grouped.has(rootDomain)) grouped.set(rootDomain, /* @__PURE__ */ new Map());
|
|
@@ -2810,10 +2344,10 @@ function generateRequiredRecords(appHostnames, rootDomain, serverIp) {
|
|
|
2810
2344
|
* Print DNS records table
|
|
2811
2345
|
*/
|
|
2812
2346
|
function printDnsRecordsTable(records, rootDomain) {
|
|
2813
|
-
logger$
|
|
2814
|
-
logger$
|
|
2815
|
-
logger$
|
|
2816
|
-
logger$
|
|
2347
|
+
logger$7.log(`\n 📋 DNS Records for ${rootDomain}:`);
|
|
2348
|
+
logger$7.log(" ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐");
|
|
2349
|
+
logger$7.log(" │ Subdomain │ Type │ Value │ Status │");
|
|
2350
|
+
logger$7.log(" ├─────────────────────────────────────┼──────┼─────────────────┼────────┤");
|
|
2817
2351
|
for (const record of records) {
|
|
2818
2352
|
const subdomain = record.subdomain.padEnd(35);
|
|
2819
2353
|
const type$1 = record.type.padEnd(4);
|
|
@@ -2823,18 +2357,18 @@ function printDnsRecordsTable(records, rootDomain) {
|
|
|
2823
2357
|
else if (record.created) status = "✓ new";
|
|
2824
2358
|
else if (record.existed) status = "✓";
|
|
2825
2359
|
else status = "?";
|
|
2826
|
-
logger$
|
|
2360
|
+
logger$7.log(` │ ${subdomain} │ ${type$1} │ ${value} │ ${status.padEnd(6)} │`);
|
|
2827
2361
|
}
|
|
2828
|
-
logger$
|
|
2362
|
+
logger$7.log(" └─────────────────────────────────────┴──────┴─────────────────┴────────┘");
|
|
2829
2363
|
}
|
|
2830
2364
|
/**
|
|
2831
2365
|
* Print DNS records in a simple format for manual setup
|
|
2832
2366
|
*/
|
|
2833
2367
|
function printDnsRecordsSimple(records, rootDomain) {
|
|
2834
|
-
logger$
|
|
2835
|
-
logger$
|
|
2836
|
-
for (const record of records) logger$
|
|
2837
|
-
logger$
|
|
2368
|
+
logger$7.log("\n 📋 Required DNS Records:");
|
|
2369
|
+
logger$7.log(` Add these A records to your DNS provider (${rootDomain}):\n`);
|
|
2370
|
+
for (const record of records) logger$7.log(` ${record.subdomain} → ${record.value} (A record)`);
|
|
2371
|
+
logger$7.log("");
|
|
2838
2372
|
}
|
|
2839
2373
|
/**
|
|
2840
2374
|
* Create DNS records for a single domain using its configured provider
|
|
@@ -2846,7 +2380,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2846
2380
|
provider = await createDnsProvider({ config: providerConfig });
|
|
2847
2381
|
} catch (error) {
|
|
2848
2382
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2849
|
-
logger$
|
|
2383
|
+
logger$7.log(` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`);
|
|
2850
2384
|
return records.map((r) => ({
|
|
2851
2385
|
...r,
|
|
2852
2386
|
error: message
|
|
@@ -2900,7 +2434,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2900
2434
|
}
|
|
2901
2435
|
} catch (error) {
|
|
2902
2436
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2903
|
-
logger$
|
|
2437
|
+
logger$7.log(` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`);
|
|
2904
2438
|
return records.map((r) => ({
|
|
2905
2439
|
hostname: r.hostname,
|
|
2906
2440
|
subdomain: r.subdomain,
|
|
@@ -2927,20 +2461,20 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2927
2461
|
async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
2928
2462
|
if (!dnsConfig) return null;
|
|
2929
2463
|
const normalizedConfig = normalizeDnsConfig(dnsConfig);
|
|
2930
|
-
logger$
|
|
2464
|
+
logger$7.log("\n🌐 Setting up DNS records...");
|
|
2931
2465
|
let serverIp;
|
|
2932
2466
|
try {
|
|
2933
2467
|
const endpointUrl = new URL(dokployEndpoint);
|
|
2934
2468
|
serverIp = await resolveHostnameToIp(endpointUrl.hostname);
|
|
2935
|
-
logger$
|
|
2469
|
+
logger$7.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);
|
|
2936
2470
|
} catch (error) {
|
|
2937
2471
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2938
|
-
logger$
|
|
2472
|
+
logger$7.log(` ⚠ Failed to resolve server IP: ${message}`);
|
|
2939
2473
|
return null;
|
|
2940
2474
|
}
|
|
2941
2475
|
const groupedHostnames = groupHostnamesByDomain(appHostnames, normalizedConfig);
|
|
2942
2476
|
if (groupedHostnames.size === 0) {
|
|
2943
|
-
logger$
|
|
2477
|
+
logger$7.log(" No DNS records needed (no hostnames match configured domains)");
|
|
2944
2478
|
return {
|
|
2945
2479
|
records: [],
|
|
2946
2480
|
success: true,
|
|
@@ -2952,22 +2486,22 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
2952
2486
|
for (const [rootDomain, domainHostnames] of groupedHostnames) {
|
|
2953
2487
|
const providerConfig = normalizedConfig[rootDomain];
|
|
2954
2488
|
if (!providerConfig) {
|
|
2955
|
-
logger$
|
|
2489
|
+
logger$7.log(` ⚠ No provider config for ${rootDomain}`);
|
|
2956
2490
|
continue;
|
|
2957
2491
|
}
|
|
2958
2492
|
const providerName = typeof providerConfig.provider === "string" ? providerConfig.provider : "custom";
|
|
2959
2493
|
const requiredRecords = generateRequiredRecords(domainHostnames, rootDomain, serverIp);
|
|
2960
2494
|
if (requiredRecords.length === 0) continue;
|
|
2961
|
-
logger$
|
|
2495
|
+
logger$7.log(` Creating DNS records for ${rootDomain} (${providerName})...`);
|
|
2962
2496
|
const domainRecords = await createDnsRecordsForDomain(requiredRecords, rootDomain, providerConfig);
|
|
2963
2497
|
allRecords.push(...domainRecords);
|
|
2964
2498
|
const created = domainRecords.filter((r) => r.created).length;
|
|
2965
2499
|
const existed = domainRecords.filter((r) => r.existed).length;
|
|
2966
2500
|
const failed = domainRecords.filter((r) => r.error).length;
|
|
2967
|
-
if (created > 0) logger$
|
|
2968
|
-
if (existed > 0) logger$
|
|
2501
|
+
if (created > 0) logger$7.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);
|
|
2502
|
+
if (existed > 0) logger$7.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);
|
|
2969
2503
|
if (failed > 0) {
|
|
2970
|
-
logger$
|
|
2504
|
+
logger$7.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
|
|
2971
2505
|
hasFailures = true;
|
|
2972
2506
|
}
|
|
2973
2507
|
if (state) {
|
|
@@ -3004,10 +2538,10 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
3004
2538
|
*/
|
|
3005
2539
|
async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
3006
2540
|
const results = [];
|
|
3007
|
-
logger$
|
|
2541
|
+
logger$7.log("\n🔍 Verifying DNS records...");
|
|
3008
2542
|
for (const [appName, hostname] of appHostnames) {
|
|
3009
2543
|
if (isDnsVerified(state, hostname, serverIp)) {
|
|
3010
|
-
logger$
|
|
2544
|
+
logger$7.log(` ✓ ${hostname} (previously verified)`);
|
|
3011
2545
|
results.push({
|
|
3012
2546
|
hostname,
|
|
3013
2547
|
appName,
|
|
@@ -3021,7 +2555,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
3021
2555
|
const resolvedIp = await resolveHostnameToIp(hostname);
|
|
3022
2556
|
if (resolvedIp === serverIp) {
|
|
3023
2557
|
setDnsVerification(state, hostname, serverIp);
|
|
3024
|
-
logger$
|
|
2558
|
+
logger$7.log(` ✓ ${hostname} → ${resolvedIp}`);
|
|
3025
2559
|
results.push({
|
|
3026
2560
|
hostname,
|
|
3027
2561
|
appName,
|
|
@@ -3030,7 +2564,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
3030
2564
|
expectedIp: serverIp
|
|
3031
2565
|
});
|
|
3032
2566
|
} else {
|
|
3033
|
-
logger$
|
|
2567
|
+
logger$7.log(` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`);
|
|
3034
2568
|
results.push({
|
|
3035
2569
|
hostname,
|
|
3036
2570
|
appName,
|
|
@@ -3041,7 +2575,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
3041
2575
|
}
|
|
3042
2576
|
} catch (error) {
|
|
3043
2577
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3044
|
-
logger$
|
|
2578
|
+
logger$7.log(` ⚠ ${hostname} DNS not propagated (${message})`);
|
|
3045
2579
|
results.push({
|
|
3046
2580
|
hostname,
|
|
3047
2581
|
appName,
|
|
@@ -3055,9 +2589,9 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
3055
2589
|
const skipped = results.filter((r) => r.skipped).length;
|
|
3056
2590
|
const pending = results.filter((r) => !r.verified).length;
|
|
3057
2591
|
if (pending > 0) {
|
|
3058
|
-
logger$
|
|
3059
|
-
logger$
|
|
3060
|
-
} else if (skipped > 0) logger$
|
|
2592
|
+
logger$7.log(`\n ${verified} verified, ${pending} pending propagation`);
|
|
2593
|
+
logger$7.log(" DNS changes may take 5-30 minutes to propagate");
|
|
2594
|
+
} else if (skipped > 0) logger$7.log(` ${verified} verified (${skipped} from cache)`);
|
|
3061
2595
|
return results;
|
|
3062
2596
|
}
|
|
3063
2597
|
|
|
@@ -4528,7 +4062,7 @@ CMD ["nginx", "-g", "daemon off;"]
|
|
|
4528
4062
|
|
|
4529
4063
|
//#endregion
|
|
4530
4064
|
//#region src/docker/index.ts
|
|
4531
|
-
const logger$
|
|
4065
|
+
const logger$6 = console;
|
|
4532
4066
|
/**
|
|
4533
4067
|
* Docker command implementation
|
|
4534
4068
|
* Generates Dockerfile, docker-compose.yml, and related files
|
|
@@ -4539,7 +4073,7 @@ const logger$7 = console;
|
|
|
4539
4073
|
async function dockerCommand(options) {
|
|
4540
4074
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
4541
4075
|
if (loadedConfig.type === "workspace") {
|
|
4542
|
-
logger$
|
|
4076
|
+
logger$6.log("📦 Detected workspace configuration");
|
|
4543
4077
|
return workspaceDockerCommand(loadedConfig.workspace, options);
|
|
4544
4078
|
}
|
|
4545
4079
|
const config = await require_config.loadConfig();
|
|
@@ -4560,14 +4094,14 @@ async function dockerCommand(options) {
|
|
|
4560
4094
|
let useTurbo = options.turbo ?? false;
|
|
4561
4095
|
if (inMonorepo && !useSlim) if (hasTurbo) {
|
|
4562
4096
|
useTurbo = true;
|
|
4563
|
-
logger$
|
|
4097
|
+
logger$6.log(" Detected monorepo with turbo.json - using turbo prune");
|
|
4564
4098
|
} else throw new Error("Monorepo detected but turbo.json not found.\n\nDocker builds in monorepos require Turborepo for proper dependency isolation.\n\nTo fix this:\n 1. Install turbo: pnpm add -Dw turbo\n 2. Create turbo.json in your monorepo root\n 3. Run this command again\n\nSee: https://turbo.build/repo/docs/guides/tools/docker");
|
|
4565
4099
|
let turboPackage = options.turboPackage ?? dockerConfig.imageName;
|
|
4566
4100
|
if (useTurbo && !options.turboPackage) try {
|
|
4567
4101
|
const pkg$1 = require(`${process.cwd()}/package.json`);
|
|
4568
4102
|
if (pkg$1.name) {
|
|
4569
4103
|
turboPackage = pkg$1.name;
|
|
4570
|
-
logger$
|
|
4104
|
+
logger$6.log(` Turbo package: ${turboPackage}`);
|
|
4571
4105
|
}
|
|
4572
4106
|
} catch {}
|
|
4573
4107
|
const templateOptions = {
|
|
@@ -4584,7 +4118,7 @@ async function dockerCommand(options) {
|
|
|
4584
4118
|
const dockerMode = useSlim ? "slim" : useTurbo ? "turbo" : "multi-stage";
|
|
4585
4119
|
const dockerfilePath = (0, node_path.join)(dockerDir, "Dockerfile");
|
|
4586
4120
|
await (0, node_fs_promises.writeFile)(dockerfilePath, dockerfile);
|
|
4587
|
-
logger$
|
|
4121
|
+
logger$6.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
|
|
4588
4122
|
const composeOptions = {
|
|
4589
4123
|
imageName: dockerConfig.imageName,
|
|
4590
4124
|
registry: options.registry ?? dockerConfig.registry,
|
|
@@ -4596,15 +4130,15 @@ async function dockerCommand(options) {
|
|
|
4596
4130
|
const dockerCompose = hasServices ? generateDockerCompose(composeOptions) : generateMinimalDockerCompose(composeOptions);
|
|
4597
4131
|
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
4598
4132
|
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
4599
|
-
logger$
|
|
4133
|
+
logger$6.log("Generated: .gkm/docker/docker-compose.yml");
|
|
4600
4134
|
const dockerignore = generateDockerignore();
|
|
4601
4135
|
const dockerignorePath = (0, node_path.join)(process.cwd(), ".dockerignore");
|
|
4602
4136
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
4603
|
-
logger$
|
|
4137
|
+
logger$6.log("Generated: .dockerignore (project root)");
|
|
4604
4138
|
const entrypoint = generateDockerEntrypoint();
|
|
4605
4139
|
const entrypointPath = (0, node_path.join)(dockerDir, "docker-entrypoint.sh");
|
|
4606
4140
|
await (0, node_fs_promises.writeFile)(entrypointPath, entrypoint);
|
|
4607
|
-
logger$
|
|
4141
|
+
logger$6.log("Generated: .gkm/docker/docker-entrypoint.sh");
|
|
4608
4142
|
const result = {
|
|
4609
4143
|
dockerfile: dockerfilePath,
|
|
4610
4144
|
dockerCompose: composePath,
|
|
@@ -4623,13 +4157,13 @@ async function dockerCommand(options) {
|
|
|
4623
4157
|
function ensureLockfile(cwd) {
|
|
4624
4158
|
const lockfilePath = findLockfilePath(cwd);
|
|
4625
4159
|
if (!lockfilePath) {
|
|
4626
|
-
logger$
|
|
4160
|
+
logger$6.warn("\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.");
|
|
4627
4161
|
return null;
|
|
4628
4162
|
}
|
|
4629
4163
|
const lockfileName = (0, node_path.basename)(lockfilePath);
|
|
4630
4164
|
const localLockfile = (0, node_path.join)(cwd, lockfileName);
|
|
4631
4165
|
if (lockfilePath === localLockfile) return null;
|
|
4632
|
-
logger$
|
|
4166
|
+
logger$6.log(` Copying ${lockfileName} from monorepo root...`);
|
|
4633
4167
|
(0, node_fs.copyFileSync)(lockfilePath, localLockfile);
|
|
4634
4168
|
return () => {
|
|
4635
4169
|
try {
|
|
@@ -4645,7 +4179,7 @@ async function buildDockerImage(imageName, options) {
|
|
|
4645
4179
|
const tag = options.tag ?? "latest";
|
|
4646
4180
|
const registry = options.registry;
|
|
4647
4181
|
const fullImageName = registry ? `${registry}/${imageName}:${tag}` : `${imageName}:${tag}`;
|
|
4648
|
-
logger$
|
|
4182
|
+
logger$6.log(`\n🐳 Building Docker image: ${fullImageName}`);
|
|
4649
4183
|
const cwd = process.cwd();
|
|
4650
4184
|
const cleanup = ensureLockfile(cwd);
|
|
4651
4185
|
try {
|
|
@@ -4657,7 +4191,7 @@ async function buildDockerImage(imageName, options) {
|
|
|
4657
4191
|
DOCKER_BUILDKIT: "1"
|
|
4658
4192
|
}
|
|
4659
4193
|
});
|
|
4660
|
-
logger$
|
|
4194
|
+
logger$6.log(`✅ Docker image built: ${fullImageName}`);
|
|
4661
4195
|
} catch (error) {
|
|
4662
4196
|
throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4663
4197
|
} finally {
|
|
@@ -4672,13 +4206,13 @@ async function pushDockerImage(imageName, options) {
|
|
|
4672
4206
|
const registry = options.registry;
|
|
4673
4207
|
if (!registry) throw new Error("Registry is required to push Docker image. Use --registry or configure docker.registry in gkm.config.ts");
|
|
4674
4208
|
const fullImageName = `${registry}/${imageName}:${tag}`;
|
|
4675
|
-
logger$
|
|
4209
|
+
logger$6.log(`\n🚀 Pushing Docker image: ${fullImageName}`);
|
|
4676
4210
|
try {
|
|
4677
4211
|
(0, node_child_process.execSync)(`docker push ${fullImageName}`, {
|
|
4678
4212
|
cwd: process.cwd(),
|
|
4679
4213
|
stdio: "inherit"
|
|
4680
4214
|
});
|
|
4681
|
-
logger$
|
|
4215
|
+
logger$6.log(`✅ Docker image pushed: ${fullImageName}`);
|
|
4682
4216
|
} catch (error) {
|
|
4683
4217
|
throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4684
4218
|
}
|
|
@@ -4704,14 +4238,14 @@ function getAppPackageName(appPath) {
|
|
|
4704
4238
|
async function workspaceDockerCommand(workspace, options) {
|
|
4705
4239
|
const results = [];
|
|
4706
4240
|
const apps = Object.entries(workspace.apps);
|
|
4707
|
-
logger$
|
|
4241
|
+
logger$6.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
|
|
4708
4242
|
const dockerDir = (0, node_path.join)(workspace.root, ".gkm", "docker");
|
|
4709
4243
|
await (0, node_fs_promises.mkdir)(dockerDir, { recursive: true });
|
|
4710
4244
|
const packageManager = detectPackageManager$1(workspace.root);
|
|
4711
|
-
logger$
|
|
4245
|
+
logger$6.log(` Package manager: ${packageManager}`);
|
|
4712
4246
|
for (const [appName, app] of apps) {
|
|
4713
4247
|
if (app.type === "mobile") {
|
|
4714
|
-
logger$
|
|
4248
|
+
logger$6.log(`\n 📱 Skipping Docker for ${appName} (mobile app — deploy via framework toolchain)`);
|
|
4715
4249
|
continue;
|
|
4716
4250
|
}
|
|
4717
4251
|
const appPath = app.path;
|
|
@@ -4720,7 +4254,7 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4720
4254
|
const imageName = appName;
|
|
4721
4255
|
const hasEntry = !!app.entry;
|
|
4722
4256
|
const buildType = hasEntry ? "entry" : app.type;
|
|
4723
|
-
logger$
|
|
4257
|
+
logger$6.log(`\n 📄 Generating Dockerfile for ${appName} (${buildType})`);
|
|
4724
4258
|
let dockerfile;
|
|
4725
4259
|
if (app.type === "web") {
|
|
4726
4260
|
const publicUrlArgs = getPublicUrlArgNames(app);
|
|
@@ -4764,7 +4298,7 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4764
4298
|
});
|
|
4765
4299
|
const dockerfilePath = (0, node_path.join)(dockerDir, `Dockerfile.${appName}`);
|
|
4766
4300
|
await (0, node_fs_promises.writeFile)(dockerfilePath, dockerfile);
|
|
4767
|
-
logger$
|
|
4301
|
+
logger$6.log(` Generated: .gkm/docker/Dockerfile.${appName}`);
|
|
4768
4302
|
results.push({
|
|
4769
4303
|
appName,
|
|
4770
4304
|
type: app.type,
|
|
@@ -4775,19 +4309,19 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4775
4309
|
const dockerignore = generateDockerignore();
|
|
4776
4310
|
const dockerignorePath = (0, node_path.join)(workspace.root, ".dockerignore");
|
|
4777
4311
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
4778
|
-
logger$
|
|
4312
|
+
logger$6.log(`\n Generated: .dockerignore (workspace root)`);
|
|
4779
4313
|
const dockerCompose = generateWorkspaceCompose(workspace, { registry: options.registry });
|
|
4780
4314
|
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
4781
4315
|
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
4782
|
-
logger$
|
|
4783
|
-
logger$
|
|
4784
|
-
logger$
|
|
4316
|
+
logger$6.log(` Generated: .gkm/docker/docker-compose.yml`);
|
|
4317
|
+
logger$6.log(`\n✅ Generated ${results.length} Dockerfile(s) + docker-compose.yml`);
|
|
4318
|
+
logger$6.log("\n📋 Build commands:");
|
|
4785
4319
|
for (const result of results) {
|
|
4786
4320
|
const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
|
|
4787
|
-
logger$
|
|
4321
|
+
logger$6.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
|
|
4788
4322
|
}
|
|
4789
|
-
logger$
|
|
4790
|
-
logger$
|
|
4323
|
+
logger$6.log("\n📋 Run all services:");
|
|
4324
|
+
logger$6.log(" docker compose -f .gkm/docker/docker-compose.yml up --build");
|
|
4791
4325
|
return {
|
|
4792
4326
|
apps: results,
|
|
4793
4327
|
dockerCompose: composePath,
|
|
@@ -4827,7 +4361,7 @@ function getAppNameFromPackageJson() {
|
|
|
4827
4361
|
} catch {}
|
|
4828
4362
|
return void 0;
|
|
4829
4363
|
}
|
|
4830
|
-
const logger$
|
|
4364
|
+
const logger$5 = console;
|
|
4831
4365
|
/**
|
|
4832
4366
|
* Get the full image reference
|
|
4833
4367
|
*/
|
|
@@ -4842,18 +4376,18 @@ function getImageRef(registry, imageName, tag) {
|
|
|
4842
4376
|
* @param buildArgs - Build arguments to pass to docker build
|
|
4843
4377
|
*/
|
|
4844
4378
|
async function buildImage(imageRef, appName, buildArgs) {
|
|
4845
|
-
logger$
|
|
4379
|
+
logger$5.log(`\n🔨 Building Docker image: ${imageRef}`);
|
|
4846
4380
|
const cwd = process.cwd();
|
|
4847
4381
|
const lockfilePath = findLockfilePath(cwd);
|
|
4848
4382
|
const lockfileDir = lockfilePath ? (0, node_path.dirname)(lockfilePath) : cwd;
|
|
4849
4383
|
const inMonorepo = lockfileDir !== cwd;
|
|
4850
|
-
if (appName || inMonorepo) logger$
|
|
4851
|
-
else logger$
|
|
4384
|
+
if (appName || inMonorepo) logger$5.log(" Generating Dockerfile for monorepo (turbo prune)...");
|
|
4385
|
+
else logger$5.log(" Generating Dockerfile...");
|
|
4852
4386
|
await dockerCommand({});
|
|
4853
4387
|
const dockerfileSuffix = appName ? `.${appName}` : "";
|
|
4854
4388
|
const dockerfilePath = `.gkm/docker/Dockerfile${dockerfileSuffix}`;
|
|
4855
4389
|
const buildCwd = lockfilePath && (inMonorepo || appName) ? lockfileDir : cwd;
|
|
4856
|
-
if (buildCwd !== cwd) logger$
|
|
4390
|
+
if (buildCwd !== cwd) logger$5.log(` Building from workspace root: ${buildCwd}`);
|
|
4857
4391
|
const buildArgsString = buildArgs && buildArgs.length > 0 ? buildArgs.map((arg) => `--build-arg "${arg}"`).join(" ") : "";
|
|
4858
4392
|
try {
|
|
4859
4393
|
const cmd = [
|
|
@@ -4872,7 +4406,7 @@ async function buildImage(imageRef, appName, buildArgs) {
|
|
|
4872
4406
|
DOCKER_BUILDKIT: "1"
|
|
4873
4407
|
}
|
|
4874
4408
|
});
|
|
4875
|
-
logger$
|
|
4409
|
+
logger$5.log(`✅ Image built: ${imageRef}`);
|
|
4876
4410
|
} catch (error) {
|
|
4877
4411
|
throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4878
4412
|
}
|
|
@@ -4881,13 +4415,13 @@ async function buildImage(imageRef, appName, buildArgs) {
|
|
|
4881
4415
|
* Push Docker image to registry
|
|
4882
4416
|
*/
|
|
4883
4417
|
async function pushImage(imageRef) {
|
|
4884
|
-
logger$
|
|
4418
|
+
logger$5.log(`\n☁️ Pushing image: ${imageRef}`);
|
|
4885
4419
|
try {
|
|
4886
4420
|
(0, node_child_process.execSync)(`docker push ${imageRef}`, {
|
|
4887
4421
|
cwd: process.cwd(),
|
|
4888
4422
|
stdio: "inherit"
|
|
4889
4423
|
});
|
|
4890
|
-
logger$
|
|
4424
|
+
logger$5.log(`✅ Image pushed: ${imageRef}`);
|
|
4891
4425
|
} catch (error) {
|
|
4892
4426
|
throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4893
4427
|
}
|
|
@@ -4900,17 +4434,17 @@ async function deployDocker(options) {
|
|
|
4900
4434
|
const imageName = config.imageName;
|
|
4901
4435
|
const imageRef = getImageRef(config.registry, imageName, tag);
|
|
4902
4436
|
await buildImage(imageRef, config.appName, buildArgs);
|
|
4903
|
-
if (!skipPush) if (!config.registry) logger$
|
|
4437
|
+
if (!skipPush) if (!config.registry) logger$5.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
4904
4438
|
else await pushImage(imageRef);
|
|
4905
|
-
logger$
|
|
4906
|
-
logger$
|
|
4907
|
-
logger$
|
|
4908
|
-
logger$
|
|
4439
|
+
logger$5.log("\n✅ Docker deployment ready!");
|
|
4440
|
+
logger$5.log(`\n📋 Deployment details:`);
|
|
4441
|
+
logger$5.log(` Image: ${imageRef}`);
|
|
4442
|
+
logger$5.log(` Stage: ${stage}`);
|
|
4909
4443
|
if (masterKey) {
|
|
4910
|
-
logger$
|
|
4911
|
-
logger$
|
|
4912
|
-
logger$
|
|
4913
|
-
logger$
|
|
4444
|
+
logger$5.log(`\n🔐 Deploy with this environment variable:`);
|
|
4445
|
+
logger$5.log(` GKM_MASTER_KEY=${masterKey}`);
|
|
4446
|
+
logger$5.log("\n Example docker run:");
|
|
4447
|
+
logger$5.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);
|
|
4914
4448
|
}
|
|
4915
4449
|
return {
|
|
4916
4450
|
imageRef,
|
|
@@ -4937,7 +4471,7 @@ function resolveDockerConfig(config) {
|
|
|
4937
4471
|
|
|
4938
4472
|
//#endregion
|
|
4939
4473
|
//#region src/deploy/dokploy.ts
|
|
4940
|
-
const logger$
|
|
4474
|
+
const logger$4 = console;
|
|
4941
4475
|
/**
|
|
4942
4476
|
* Get the Dokploy API token from stored credentials or environment
|
|
4943
4477
|
*/
|
|
@@ -4961,25 +4495,25 @@ async function createApi$1(endpoint) {
|
|
|
4961
4495
|
*/
|
|
4962
4496
|
async function deployDokploy(options) {
|
|
4963
4497
|
const { stage, imageRef, masterKey, config } = options;
|
|
4964
|
-
logger$
|
|
4965
|
-
logger$
|
|
4966
|
-
logger$
|
|
4498
|
+
logger$4.log(`\n🎯 Deploying to Dokploy...`);
|
|
4499
|
+
logger$4.log(` Endpoint: ${config.endpoint}`);
|
|
4500
|
+
logger$4.log(` Application: ${config.applicationId}`);
|
|
4967
4501
|
const api = await createApi$1(config.endpoint);
|
|
4968
|
-
logger$
|
|
4502
|
+
logger$4.log(` Configuring Docker image: ${imageRef}`);
|
|
4969
4503
|
const registryOptions = {};
|
|
4970
4504
|
if (config.registryId) {
|
|
4971
4505
|
registryOptions.registryId = config.registryId;
|
|
4972
|
-
logger$
|
|
4506
|
+
logger$4.log(` Using Dokploy registry: ${config.registryId}`);
|
|
4973
4507
|
} else {
|
|
4974
4508
|
const storedRegistryId = await require_credentials.getDokployRegistryId();
|
|
4975
4509
|
if (storedRegistryId) {
|
|
4976
4510
|
registryOptions.registryId = storedRegistryId;
|
|
4977
|
-
logger$
|
|
4511
|
+
logger$4.log(` Using stored Dokploy registry: ${storedRegistryId}`);
|
|
4978
4512
|
} else if (config.registryCredentials) {
|
|
4979
4513
|
registryOptions.username = config.registryCredentials.username;
|
|
4980
4514
|
registryOptions.password = config.registryCredentials.password;
|
|
4981
4515
|
registryOptions.registryUrl = config.registryCredentials.registryUrl;
|
|
4982
|
-
logger$
|
|
4516
|
+
logger$4.log(` Using registry credentials for: ${config.registryCredentials.registryUrl}`);
|
|
4983
4517
|
} else {
|
|
4984
4518
|
const username = process.env.DOCKER_REGISTRY_USERNAME;
|
|
4985
4519
|
const password = process.env.DOCKER_REGISTRY_PASSWORD;
|
|
@@ -4988,31 +4522,31 @@ async function deployDokploy(options) {
|
|
|
4988
4522
|
registryOptions.username = username;
|
|
4989
4523
|
registryOptions.password = password;
|
|
4990
4524
|
registryOptions.registryUrl = registryUrl;
|
|
4991
|
-
logger$
|
|
4525
|
+
logger$4.log(` Using registry credentials from environment`);
|
|
4992
4526
|
}
|
|
4993
4527
|
}
|
|
4994
4528
|
}
|
|
4995
4529
|
await api.saveDockerProvider(config.applicationId, imageRef, registryOptions);
|
|
4996
|
-
logger$
|
|
4530
|
+
logger$4.log(" ✓ Docker provider configured");
|
|
4997
4531
|
const envVars = {};
|
|
4998
4532
|
if (masterKey) envVars.GKM_MASTER_KEY = masterKey;
|
|
4999
4533
|
if (Object.keys(envVars).length > 0) {
|
|
5000
|
-
logger$
|
|
4534
|
+
logger$4.log(" Updating environment variables...");
|
|
5001
4535
|
const envString = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
5002
4536
|
await api.saveApplicationEnv(config.applicationId, envString);
|
|
5003
|
-
logger$
|
|
4537
|
+
logger$4.log(" ✓ Environment variables updated");
|
|
5004
4538
|
}
|
|
5005
|
-
logger$
|
|
4539
|
+
logger$4.log(" Triggering deployment...");
|
|
5006
4540
|
await api.deployApplication(config.applicationId);
|
|
5007
|
-
logger$
|
|
5008
|
-
logger$
|
|
5009
|
-
logger$
|
|
5010
|
-
logger$
|
|
5011
|
-
logger$
|
|
5012
|
-
logger$
|
|
5013
|
-
if (masterKey) logger$
|
|
4541
|
+
logger$4.log(" ✓ Deployment triggered");
|
|
4542
|
+
logger$4.log("\n✅ Dokploy deployment initiated!");
|
|
4543
|
+
logger$4.log(`\n📋 Deployment details:`);
|
|
4544
|
+
logger$4.log(` Image: ${imageRef}`);
|
|
4545
|
+
logger$4.log(` Stage: ${stage}`);
|
|
4546
|
+
logger$4.log(` Application ID: ${config.applicationId}`);
|
|
4547
|
+
if (masterKey) logger$4.log(`\n🔐 GKM_MASTER_KEY has been set in Dokploy environment`);
|
|
5014
4548
|
const deploymentUrl = `${config.endpoint}/project/${config.projectId}`;
|
|
5015
|
-
logger$
|
|
4549
|
+
logger$4.log(`\n🔗 View deployment: ${deploymentUrl}`);
|
|
5016
4550
|
return {
|
|
5017
4551
|
imageRef,
|
|
5018
4552
|
masterKey,
|
|
@@ -5129,7 +4663,7 @@ function validateEnvVars(requiredVars, context) {
|
|
|
5129
4663
|
|
|
5130
4664
|
//#endregion
|
|
5131
4665
|
//#region src/deploy/init.ts
|
|
5132
|
-
const logger$
|
|
4666
|
+
const logger$3 = console;
|
|
5133
4667
|
/**
|
|
5134
4668
|
* Get the Dokploy API token from stored credentials or environment
|
|
5135
4669
|
*/
|
|
@@ -5163,20 +4697,20 @@ async function createApi(endpoint) {
|
|
|
5163
4697
|
async function updateConfig(config, cwd = process.cwd()) {
|
|
5164
4698
|
const configPath = (0, node_path.join)(cwd, "gkm.config.ts");
|
|
5165
4699
|
if (!(0, node_fs.existsSync)(configPath)) {
|
|
5166
|
-
logger$
|
|
5167
|
-
logger$
|
|
5168
|
-
logger$
|
|
5169
|
-
logger$
|
|
5170
|
-
logger$
|
|
5171
|
-
logger$
|
|
5172
|
-
logger$
|
|
5173
|
-
logger$
|
|
4700
|
+
logger$3.warn("\n gkm.config.ts not found. Add this configuration manually:\n");
|
|
4701
|
+
logger$3.log(` providers: {`);
|
|
4702
|
+
logger$3.log(` dokploy: {`);
|
|
4703
|
+
logger$3.log(` endpoint: '${config.endpoint}',`);
|
|
4704
|
+
logger$3.log(` projectId: '${config.projectId}',`);
|
|
4705
|
+
logger$3.log(` applicationId: '${config.applicationId}',`);
|
|
4706
|
+
logger$3.log(` },`);
|
|
4707
|
+
logger$3.log(` },`);
|
|
5174
4708
|
return;
|
|
5175
4709
|
}
|
|
5176
4710
|
const content = await (0, node_fs_promises.readFile)(configPath, "utf-8");
|
|
5177
4711
|
if (content.includes("dokploy:") && content.includes("applicationId:")) {
|
|
5178
|
-
logger$
|
|
5179
|
-
logger$
|
|
4712
|
+
logger$3.log("\n Dokploy config already exists in gkm.config.ts");
|
|
4713
|
+
logger$3.log(" Updating with new values...");
|
|
5180
4714
|
}
|
|
5181
4715
|
const registryLine = config.registryId ? `\n\t\t\tregistryId: '${config.registryId}',` : "";
|
|
5182
4716
|
const dokployConfigStr = `dokploy: {
|
|
@@ -5193,7 +4727,7 @@ async function updateConfig(config, cwd = process.cwd()) {
|
|
|
5193
4727
|
},
|
|
5194
4728
|
});`);
|
|
5195
4729
|
await (0, node_fs_promises.writeFile)(configPath, newContent);
|
|
5196
|
-
logger$
|
|
4730
|
+
logger$3.log("\n ✓ Updated gkm.config.ts with Dokploy configuration");
|
|
5197
4731
|
}
|
|
5198
4732
|
/**
|
|
5199
4733
|
* Initialize Dokploy deployment configuration
|
|
@@ -5202,24 +4736,24 @@ async function deployInitCommand(options) {
|
|
|
5202
4736
|
const { projectName, appName, projectId: existingProjectId, registryId } = options;
|
|
5203
4737
|
const endpoint = await getEndpoint(options.endpoint);
|
|
5204
4738
|
const api = await createApi(endpoint);
|
|
5205
|
-
logger$
|
|
5206
|
-
logger$
|
|
4739
|
+
logger$3.log(`\n🚀 Initializing Dokploy deployment...`);
|
|
4740
|
+
logger$3.log(` Endpoint: ${endpoint}`);
|
|
5207
4741
|
let projectId;
|
|
5208
4742
|
if (existingProjectId) {
|
|
5209
4743
|
projectId = existingProjectId;
|
|
5210
|
-
logger$
|
|
4744
|
+
logger$3.log(`\n📁 Using existing project: ${projectId}`);
|
|
5211
4745
|
} else {
|
|
5212
|
-
logger$
|
|
4746
|
+
logger$3.log(`\n📁 Looking for project: ${projectName}`);
|
|
5213
4747
|
const projects = await api.listProjects();
|
|
5214
4748
|
const existingProject = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
5215
4749
|
if (existingProject) {
|
|
5216
4750
|
projectId = existingProject.projectId;
|
|
5217
|
-
logger$
|
|
4751
|
+
logger$3.log(` Found existing project: ${projectId}`);
|
|
5218
4752
|
} else {
|
|
5219
|
-
logger$
|
|
4753
|
+
logger$3.log(` Creating new project...`);
|
|
5220
4754
|
const result = await api.createProject(projectName);
|
|
5221
4755
|
projectId = result.project.projectId;
|
|
5222
|
-
logger$
|
|
4756
|
+
logger$3.log(` ✓ Created project: ${projectId}`);
|
|
5223
4757
|
}
|
|
5224
4758
|
}
|
|
5225
4759
|
const project = await api.getProject(projectId);
|
|
@@ -5227,23 +4761,23 @@ async function deployInitCommand(options) {
|
|
|
5227
4761
|
const firstEnv = project.environments?.[0];
|
|
5228
4762
|
if (firstEnv) environmentId = firstEnv.environmentId;
|
|
5229
4763
|
else {
|
|
5230
|
-
logger$
|
|
4764
|
+
logger$3.log(` Creating production environment...`);
|
|
5231
4765
|
const env = await api.createEnvironment(projectId, "production");
|
|
5232
4766
|
environmentId = env.environmentId;
|
|
5233
4767
|
}
|
|
5234
|
-
logger$
|
|
4768
|
+
logger$3.log(`\n📦 Creating application: ${appName}`);
|
|
5235
4769
|
const application = await api.createApplication(appName, projectId, environmentId);
|
|
5236
|
-
logger$
|
|
4770
|
+
logger$3.log(` ✓ Created application: ${application.applicationId}`);
|
|
5237
4771
|
if (registryId) {
|
|
5238
|
-
logger$
|
|
4772
|
+
logger$3.log(`\n🔧 Configuring registry: ${registryId}`);
|
|
5239
4773
|
await api.updateApplication(application.applicationId, { registryId });
|
|
5240
|
-
logger$
|
|
4774
|
+
logger$3.log(` ✓ Registry configured`);
|
|
5241
4775
|
} else try {
|
|
5242
4776
|
const registries = await api.listRegistries();
|
|
5243
4777
|
if (registries.length > 0) {
|
|
5244
|
-
logger$
|
|
5245
|
-
for (const reg of registries) logger$
|
|
5246
|
-
logger$
|
|
4778
|
+
logger$3.log(`\n📋 Available registries:`);
|
|
4779
|
+
for (const reg of registries) logger$3.log(` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`);
|
|
4780
|
+
logger$3.log(`\n To use a registry, run with --registry-id <id>`);
|
|
5247
4781
|
}
|
|
5248
4782
|
} catch {}
|
|
5249
4783
|
const config = {
|
|
@@ -5252,14 +4786,14 @@ async function deployInitCommand(options) {
|
|
|
5252
4786
|
applicationId: application.applicationId
|
|
5253
4787
|
};
|
|
5254
4788
|
await updateConfig(config);
|
|
5255
|
-
logger$
|
|
5256
|
-
logger$
|
|
5257
|
-
logger$
|
|
5258
|
-
logger$
|
|
5259
|
-
logger$
|
|
5260
|
-
logger$
|
|
5261
|
-
logger$
|
|
5262
|
-
logger$
|
|
4789
|
+
logger$3.log(`\n✅ Dokploy deployment initialized!`);
|
|
4790
|
+
logger$3.log(`\n📋 Configuration:`);
|
|
4791
|
+
logger$3.log(` Project ID: ${projectId}`);
|
|
4792
|
+
logger$3.log(` Application ID: ${application.applicationId}`);
|
|
4793
|
+
logger$3.log(`\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);
|
|
4794
|
+
logger$3.log(`\n📝 Next steps:`);
|
|
4795
|
+
logger$3.log(` 1. Initialize secrets: gkm secrets:init --stage production`);
|
|
4796
|
+
logger$3.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);
|
|
5263
4797
|
return config;
|
|
5264
4798
|
}
|
|
5265
4799
|
/**
|
|
@@ -5270,32 +4804,32 @@ async function deployListCommand(options) {
|
|
|
5270
4804
|
const api = await createApi(endpoint);
|
|
5271
4805
|
const { resource } = options;
|
|
5272
4806
|
if (resource === "projects") {
|
|
5273
|
-
logger$
|
|
4807
|
+
logger$3.log(`\n📁 Projects in ${endpoint}:`);
|
|
5274
4808
|
const projects = await api.listProjects();
|
|
5275
4809
|
if (projects.length === 0) {
|
|
5276
|
-
logger$
|
|
4810
|
+
logger$3.log(" No projects found");
|
|
5277
4811
|
return;
|
|
5278
4812
|
}
|
|
5279
4813
|
for (const project of projects) {
|
|
5280
|
-
logger$
|
|
5281
|
-
if (project.description) logger$
|
|
4814
|
+
logger$3.log(`\n ${project.name} (${project.projectId})`);
|
|
4815
|
+
if (project.description) logger$3.log(` ${project.description}`);
|
|
5282
4816
|
}
|
|
5283
4817
|
} else if (resource === "registries") {
|
|
5284
|
-
logger$
|
|
4818
|
+
logger$3.log(`\n🐳 Registries in ${endpoint}:`);
|
|
5285
4819
|
const registries = await api.listRegistries();
|
|
5286
4820
|
if (registries.length === 0) {
|
|
5287
|
-
logger$
|
|
5288
|
-
logger$
|
|
4821
|
+
logger$3.log(" No registries configured");
|
|
4822
|
+
logger$3.log(" Run \"gkm registry:setup\" to configure a registry");
|
|
5289
4823
|
return;
|
|
5290
4824
|
}
|
|
5291
4825
|
const storedRegistryId = await require_credentials.getDokployRegistryId();
|
|
5292
4826
|
for (const registry of registries) {
|
|
5293
4827
|
const isDefault = registry.registryId === storedRegistryId;
|
|
5294
4828
|
const marker = isDefault ? " (default)" : "";
|
|
5295
|
-
logger$
|
|
5296
|
-
logger$
|
|
5297
|
-
logger$
|
|
5298
|
-
if (registry.imagePrefix) logger$
|
|
4829
|
+
logger$3.log(`\n ${registry.registryName}${marker} (${registry.registryId})`);
|
|
4830
|
+
logger$3.log(` URL: ${registry.registryUrl}`);
|
|
4831
|
+
logger$3.log(` Username: ${registry.username}`);
|
|
4832
|
+
if (registry.imagePrefix) logger$3.log(` Prefix: ${registry.imagePrefix}`);
|
|
5299
4833
|
}
|
|
5300
4834
|
}
|
|
5301
4835
|
}
|
|
@@ -5318,20 +4852,20 @@ function isStateProvider(value) {
|
|
|
5318
4852
|
async function createStateProvider(options) {
|
|
5319
4853
|
const { config, workspaceRoot, workspaceName } = options;
|
|
5320
4854
|
if (!config) {
|
|
5321
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
4855
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CalzNfNA.cjs"));
|
|
5322
4856
|
return new LocalStateProvider(workspaceRoot);
|
|
5323
4857
|
}
|
|
5324
4858
|
if (isStateProvider(config.provider)) return config.provider;
|
|
5325
4859
|
const provider = config.provider;
|
|
5326
4860
|
if (provider === "local") {
|
|
5327
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
4861
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CalzNfNA.cjs"));
|
|
5328
4862
|
return new LocalStateProvider(workspaceRoot);
|
|
5329
4863
|
}
|
|
5330
4864
|
if (provider === "ssm") {
|
|
5331
4865
|
if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
|
|
5332
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
5333
|
-
const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-
|
|
5334
|
-
const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-
|
|
4866
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CalzNfNA.cjs"));
|
|
4867
|
+
const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-Bt6r8CTL.cjs"));
|
|
4868
|
+
const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-BdX7klRc.cjs"));
|
|
5335
4869
|
const ssmConfig = config;
|
|
5336
4870
|
const local = new LocalStateProvider(workspaceRoot);
|
|
5337
4871
|
const ssm = SSMStateProvider.create({
|
|
@@ -5791,7 +5325,7 @@ async function sniffAllApps(apps, workspacePath, options = {}) {
|
|
|
5791
5325
|
|
|
5792
5326
|
//#endregion
|
|
5793
5327
|
//#region src/deploy/index.ts
|
|
5794
|
-
const logger$
|
|
5328
|
+
const logger$2 = console;
|
|
5795
5329
|
/**
|
|
5796
5330
|
* Prompt for input
|
|
5797
5331
|
*/
|
|
@@ -5799,7 +5333,7 @@ async function prompt(message, hidden = false) {
|
|
|
5799
5333
|
if (!process.stdin.isTTY) throw new Error("Interactive input required. Please configure manually.");
|
|
5800
5334
|
if (hidden) {
|
|
5801
5335
|
process.stdout.write(message);
|
|
5802
|
-
return new Promise((resolve$
|
|
5336
|
+
return new Promise((resolve$3) => {
|
|
5803
5337
|
let value = "";
|
|
5804
5338
|
const onData = (char) => {
|
|
5805
5339
|
const c = char.toString();
|
|
@@ -5808,7 +5342,7 @@ async function prompt(message, hidden = false) {
|
|
|
5808
5342
|
process.stdin.pause();
|
|
5809
5343
|
process.stdin.removeListener("data", onData);
|
|
5810
5344
|
process.stdout.write("\n");
|
|
5811
|
-
resolve$
|
|
5345
|
+
resolve$3(value);
|
|
5812
5346
|
} else if (c === "") {
|
|
5813
5347
|
process.stdin.setRawMode(false);
|
|
5814
5348
|
process.stdin.pause();
|
|
@@ -5863,7 +5397,7 @@ async function waitForPostgres(host, port, user, password, database, maxRetries
|
|
|
5863
5397
|
return;
|
|
5864
5398
|
} catch {
|
|
5865
5399
|
if (i < maxRetries - 1) {
|
|
5866
|
-
logger$
|
|
5400
|
+
logger$2.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);
|
|
5867
5401
|
await new Promise((r) => setTimeout(r, retryIntervalMs));
|
|
5868
5402
|
}
|
|
5869
5403
|
}
|
|
@@ -5898,12 +5432,12 @@ async function waitForPostgres(host, port, user, password, database, maxRetries
|
|
|
5898
5432
|
* ```
|
|
5899
5433
|
*/
|
|
5900
5434
|
async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
5901
|
-
logger$
|
|
5435
|
+
logger$2.log("\n🔧 Initializing database users...");
|
|
5902
5436
|
const externalPort = 5432;
|
|
5903
|
-
logger$
|
|
5437
|
+
logger$2.log(` Enabling external port ${externalPort}...`);
|
|
5904
5438
|
await api.savePostgresExternalPort(postgres.postgresId, externalPort);
|
|
5905
5439
|
await api.deployPostgres(postgres.postgresId);
|
|
5906
|
-
logger$
|
|
5440
|
+
logger$2.log(` Waiting for Postgres to be accessible at ${serverHostname}:${externalPort}...`);
|
|
5907
5441
|
await waitForPostgres(serverHostname, externalPort, postgres.databaseUser, postgres.databasePassword, postgres.databaseName);
|
|
5908
5442
|
const client = new pg.Client({
|
|
5909
5443
|
host: serverHostname,
|
|
@@ -5916,7 +5450,7 @@ async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
|
5916
5450
|
await client.connect();
|
|
5917
5451
|
for (const user of users) {
|
|
5918
5452
|
const schemaName = user.usePublicSchema ? "public" : user.name;
|
|
5919
|
-
logger$
|
|
5453
|
+
logger$2.log(` Creating user "${user.name}" with schema "${schemaName}"...`);
|
|
5920
5454
|
if (user.usePublicSchema) {
|
|
5921
5455
|
await client.query(`
|
|
5922
5456
|
DO $$ BEGIN
|
|
@@ -5951,15 +5485,15 @@ async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
|
5951
5485
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "${schemaName}" GRANT ALL ON TABLES TO "${user.name}";
|
|
5952
5486
|
`);
|
|
5953
5487
|
}
|
|
5954
|
-
logger$
|
|
5488
|
+
logger$2.log(` ✓ User "${user.name}" configured`);
|
|
5955
5489
|
}
|
|
5956
5490
|
} finally {
|
|
5957
5491
|
await client.end();
|
|
5958
5492
|
}
|
|
5959
|
-
logger$
|
|
5493
|
+
logger$2.log(" Disabling external port...");
|
|
5960
5494
|
await api.savePostgresExternalPort(postgres.postgresId, null);
|
|
5961
5495
|
await api.deployPostgres(postgres.postgresId);
|
|
5962
|
-
logger$
|
|
5496
|
+
logger$2.log(" ✓ Database users initialized");
|
|
5963
5497
|
}
|
|
5964
5498
|
/**
|
|
5965
5499
|
* Get the server hostname from the Dokploy endpoint URL
|
|
@@ -5973,24 +5507,24 @@ function getServerHostname(endpoint) {
|
|
|
5973
5507
|
* @internal Exported for testing
|
|
5974
5508
|
*/
|
|
5975
5509
|
async function provisionServices(api, projectId, environmentId, projectName, services, existingServiceIds) {
|
|
5976
|
-
logger$
|
|
5510
|
+
logger$2.log(`\n🔍 provisionServices called: services=${JSON.stringify(services)}, envId=${environmentId}`);
|
|
5977
5511
|
if (!services || !environmentId) {
|
|
5978
|
-
logger$
|
|
5512
|
+
logger$2.log(" Skipping: no services or no environmentId");
|
|
5979
5513
|
return void 0;
|
|
5980
5514
|
}
|
|
5981
5515
|
const serviceUrls = {};
|
|
5982
5516
|
const serviceIds = {};
|
|
5983
5517
|
if (services.postgres) {
|
|
5984
|
-
logger$
|
|
5518
|
+
logger$2.log("\n🐘 Checking PostgreSQL...");
|
|
5985
5519
|
const postgresName = "db";
|
|
5986
5520
|
try {
|
|
5987
5521
|
let postgres = null;
|
|
5988
5522
|
let created = false;
|
|
5989
5523
|
if (existingServiceIds?.postgresId) {
|
|
5990
|
-
logger$
|
|
5524
|
+
logger$2.log(` Using cached ID: ${existingServiceIds.postgresId}`);
|
|
5991
5525
|
postgres = await api.getPostgres(existingServiceIds.postgresId);
|
|
5992
|
-
if (postgres) logger$
|
|
5993
|
-
else logger$
|
|
5526
|
+
if (postgres) logger$2.log(` ✓ PostgreSQL found: ${postgres.postgresId}`);
|
|
5527
|
+
else logger$2.log(` ⚠ Cached ID invalid, will create new`);
|
|
5994
5528
|
}
|
|
5995
5529
|
if (!postgres) {
|
|
5996
5530
|
const databasePassword = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
@@ -6002,10 +5536,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
6002
5536
|
postgres = result.postgres;
|
|
6003
5537
|
created = result.created;
|
|
6004
5538
|
if (created) {
|
|
6005
|
-
logger$
|
|
5539
|
+
logger$2.log(` ✓ Created PostgreSQL: ${postgres.postgresId}`);
|
|
6006
5540
|
await api.deployPostgres(postgres.postgresId);
|
|
6007
|
-
logger$
|
|
6008
|
-
} else logger$
|
|
5541
|
+
logger$2.log(" ✓ PostgreSQL deployed");
|
|
5542
|
+
} else logger$2.log(` ✓ PostgreSQL already exists: ${postgres.postgresId}`);
|
|
6009
5543
|
}
|
|
6010
5544
|
serviceIds.postgresId = postgres.postgresId;
|
|
6011
5545
|
serviceUrls.DATABASE_HOST = postgres.appName;
|
|
@@ -6014,23 +5548,23 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
6014
5548
|
serviceUrls.DATABASE_USER = postgres.databaseUser;
|
|
6015
5549
|
serviceUrls.DATABASE_PASSWORD = postgres.databasePassword;
|
|
6016
5550
|
serviceUrls.DATABASE_URL = `postgresql://${postgres.databaseUser}:${postgres.databasePassword}@${postgres.appName}:5432/${postgres.databaseName}`;
|
|
6017
|
-
logger$
|
|
5551
|
+
logger$2.log(` ✓ Database credentials configured`);
|
|
6018
5552
|
} catch (error) {
|
|
6019
5553
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6020
|
-
logger$
|
|
5554
|
+
logger$2.log(` ⚠ Failed to provision PostgreSQL: ${message}`);
|
|
6021
5555
|
}
|
|
6022
5556
|
}
|
|
6023
5557
|
if (services.redis) {
|
|
6024
|
-
logger$
|
|
5558
|
+
logger$2.log("\n🔴 Checking Redis...");
|
|
6025
5559
|
const redisName = "cache";
|
|
6026
5560
|
try {
|
|
6027
5561
|
let redis = null;
|
|
6028
5562
|
let created = false;
|
|
6029
5563
|
if (existingServiceIds?.redisId) {
|
|
6030
|
-
logger$
|
|
5564
|
+
logger$2.log(` Using cached ID: ${existingServiceIds.redisId}`);
|
|
6031
5565
|
redis = await api.getRedis(existingServiceIds.redisId);
|
|
6032
|
-
if (redis) logger$
|
|
6033
|
-
else logger$
|
|
5566
|
+
if (redis) logger$2.log(` ✓ Redis found: ${redis.redisId}`);
|
|
5567
|
+
else logger$2.log(` ⚠ Cached ID invalid, will create new`);
|
|
6034
5568
|
}
|
|
6035
5569
|
if (!redis) {
|
|
6036
5570
|
const { randomBytes: randomBytes$2 } = await import("node:crypto");
|
|
@@ -6039,10 +5573,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
6039
5573
|
redis = result.redis;
|
|
6040
5574
|
created = result.created;
|
|
6041
5575
|
if (created) {
|
|
6042
|
-
logger$
|
|
5576
|
+
logger$2.log(` ✓ Created Redis: ${redis.redisId}`);
|
|
6043
5577
|
await api.deployRedis(redis.redisId);
|
|
6044
|
-
logger$
|
|
6045
|
-
} else logger$
|
|
5578
|
+
logger$2.log(" ✓ Redis deployed");
|
|
5579
|
+
} else logger$2.log(` ✓ Redis already exists: ${redis.redisId}`);
|
|
6046
5580
|
}
|
|
6047
5581
|
serviceIds.redisId = redis.redisId;
|
|
6048
5582
|
serviceUrls.REDIS_HOST = redis.appName;
|
|
@@ -6050,10 +5584,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
6050
5584
|
if (redis.databasePassword) serviceUrls.REDIS_PASSWORD = redis.databasePassword;
|
|
6051
5585
|
const password = redis.databasePassword ? `:${redis.databasePassword}@` : "";
|
|
6052
5586
|
serviceUrls.REDIS_URL = `redis://${password}${redis.appName}:6379`;
|
|
6053
|
-
logger$
|
|
5587
|
+
logger$2.log(` ✓ Redis credentials configured`);
|
|
6054
5588
|
} catch (error) {
|
|
6055
5589
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6056
|
-
logger$
|
|
5590
|
+
logger$2.log(` ⚠ Failed to provision Redis: ${message}`);
|
|
6057
5591
|
}
|
|
6058
5592
|
}
|
|
6059
5593
|
return Object.keys(serviceUrls).length > 0 ? {
|
|
@@ -6065,10 +5599,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
6065
5599
|
* Ensure Dokploy is fully configured, recovering/creating resources as needed
|
|
6066
5600
|
*/
|
|
6067
5601
|
async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
6068
|
-
logger$
|
|
5602
|
+
logger$2.log("\n🔧 Checking Dokploy setup...");
|
|
6069
5603
|
let creds = await require_credentials.getDokployCredentials();
|
|
6070
5604
|
if (!creds) {
|
|
6071
|
-
logger$
|
|
5605
|
+
logger$2.log("\n📋 Dokploy credentials not found. Let's set them up.");
|
|
6072
5606
|
const endpoint = await prompt("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
6073
5607
|
const normalizedEndpoint = endpoint.replace(/\/$/, "");
|
|
6074
5608
|
try {
|
|
@@ -6076,9 +5610,9 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
6076
5610
|
} catch {
|
|
6077
5611
|
throw new Error("Invalid URL format");
|
|
6078
5612
|
}
|
|
6079
|
-
logger$
|
|
5613
|
+
logger$2.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
|
|
6080
5614
|
const token = await prompt("API Token: ", true);
|
|
6081
|
-
logger$
|
|
5615
|
+
logger$2.log("\nValidating credentials...");
|
|
6082
5616
|
const isValid = await validateDokployToken(normalizedEndpoint, token);
|
|
6083
5617
|
if (!isValid) throw new Error("Invalid credentials. Please check your token.");
|
|
6084
5618
|
await require_credentials.storeDokployCredentials(token, normalizedEndpoint);
|
|
@@ -6086,7 +5620,7 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
6086
5620
|
token,
|
|
6087
5621
|
endpoint: normalizedEndpoint
|
|
6088
5622
|
};
|
|
6089
|
-
logger$
|
|
5623
|
+
logger$2.log("✓ Credentials saved");
|
|
6090
5624
|
}
|
|
6091
5625
|
const api = new require_dokploy_api.DokployApi({
|
|
6092
5626
|
baseUrl: creds.endpoint,
|
|
@@ -6094,20 +5628,20 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
6094
5628
|
});
|
|
6095
5629
|
const existingConfig = config.providers?.dokploy;
|
|
6096
5630
|
if (existingConfig && typeof existingConfig !== "boolean" && existingConfig.applicationId && existingConfig.projectId) {
|
|
6097
|
-
logger$
|
|
5631
|
+
logger$2.log("✓ Dokploy config found in gkm.config.ts");
|
|
6098
5632
|
try {
|
|
6099
5633
|
const projectDetails = await api.getProject(existingConfig.projectId);
|
|
6100
|
-
logger$
|
|
5634
|
+
logger$2.log("✓ Project verified");
|
|
6101
5635
|
const storedRegistryId = existingConfig.registryId ?? await require_credentials.getDokployRegistryId();
|
|
6102
5636
|
const environments = projectDetails.environments ?? [];
|
|
6103
5637
|
let environment = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
6104
5638
|
if (!environment) {
|
|
6105
|
-
logger$
|
|
5639
|
+
logger$2.log(` Creating "${stage}" environment...`);
|
|
6106
5640
|
environment = await api.createEnvironment(existingConfig.projectId, stage);
|
|
6107
|
-
logger$
|
|
5641
|
+
logger$2.log(` ✓ Created environment: ${environment.environmentId}`);
|
|
6108
5642
|
}
|
|
6109
5643
|
const environmentId$1 = environment.environmentId;
|
|
6110
|
-
logger$
|
|
5644
|
+
logger$2.log(` Services config: ${JSON.stringify(services)}, envId: ${environmentId$1}`);
|
|
6111
5645
|
const provisionResult$1 = await provisionServices(api, existingConfig.projectId, environmentId$1, dockerConfig.appName, services, void 0);
|
|
6112
5646
|
return {
|
|
6113
5647
|
config: {
|
|
@@ -6120,97 +5654,97 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
6120
5654
|
serviceUrls: provisionResult$1?.serviceUrls
|
|
6121
5655
|
};
|
|
6122
5656
|
} catch {
|
|
6123
|
-
logger$
|
|
5657
|
+
logger$2.log("⚠ Project not found, will recover...");
|
|
6124
5658
|
}
|
|
6125
5659
|
}
|
|
6126
|
-
logger$
|
|
5660
|
+
logger$2.log("\n📁 Looking for project...");
|
|
6127
5661
|
const projectName = dockerConfig.projectName;
|
|
6128
5662
|
const projects = await api.listProjects();
|
|
6129
5663
|
let project = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
6130
5664
|
let environmentId;
|
|
6131
5665
|
if (project) {
|
|
6132
|
-
logger$
|
|
5666
|
+
logger$2.log(` Found existing project: ${project.name} (${project.projectId})`);
|
|
6133
5667
|
const projectDetails = await api.getProject(project.projectId);
|
|
6134
5668
|
const environments = projectDetails.environments ?? [];
|
|
6135
5669
|
const matchingEnv = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
6136
5670
|
if (matchingEnv) {
|
|
6137
5671
|
environmentId = matchingEnv.environmentId;
|
|
6138
|
-
logger$
|
|
5672
|
+
logger$2.log(` Using environment: ${matchingEnv.name}`);
|
|
6139
5673
|
} else {
|
|
6140
|
-
logger$
|
|
5674
|
+
logger$2.log(` Creating "${stage}" environment...`);
|
|
6141
5675
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
6142
5676
|
environmentId = env.environmentId;
|
|
6143
|
-
logger$
|
|
5677
|
+
logger$2.log(` ✓ Created environment: ${stage}`);
|
|
6144
5678
|
}
|
|
6145
5679
|
} else {
|
|
6146
|
-
logger$
|
|
5680
|
+
logger$2.log(` Creating project: ${projectName}`);
|
|
6147
5681
|
const result = await api.createProject(projectName);
|
|
6148
5682
|
project = result.project;
|
|
6149
5683
|
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
6150
|
-
logger$
|
|
5684
|
+
logger$2.log(` Creating "${stage}" environment...`);
|
|
6151
5685
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
6152
5686
|
environmentId = env.environmentId;
|
|
6153
5687
|
} else environmentId = result.environment.environmentId;
|
|
6154
|
-
logger$
|
|
6155
|
-
logger$
|
|
5688
|
+
logger$2.log(` ✓ Created project: ${project.projectId}`);
|
|
5689
|
+
logger$2.log(` ✓ Using environment: ${stage}`);
|
|
6156
5690
|
}
|
|
6157
|
-
logger$
|
|
5691
|
+
logger$2.log("\n📦 Looking for application...");
|
|
6158
5692
|
const appName = dockerConfig.appName;
|
|
6159
5693
|
let applicationId;
|
|
6160
5694
|
if (existingConfig && typeof existingConfig !== "boolean" && existingConfig.applicationId) {
|
|
6161
5695
|
applicationId = existingConfig.applicationId;
|
|
6162
|
-
logger$
|
|
5696
|
+
logger$2.log(` Using application from config: ${applicationId}`);
|
|
6163
5697
|
} else {
|
|
6164
|
-
logger$
|
|
5698
|
+
logger$2.log(` Creating application: ${appName}`);
|
|
6165
5699
|
const app = await api.createApplication(appName, project.projectId, environmentId);
|
|
6166
5700
|
applicationId = app.applicationId;
|
|
6167
|
-
logger$
|
|
5701
|
+
logger$2.log(` ✓ Created application: ${applicationId}`);
|
|
6168
5702
|
}
|
|
6169
|
-
logger$
|
|
5703
|
+
logger$2.log("\n🐳 Checking registry...");
|
|
6170
5704
|
let registryId = await require_credentials.getDokployRegistryId();
|
|
6171
5705
|
if (registryId) try {
|
|
6172
5706
|
const registry = await api.getRegistry(registryId);
|
|
6173
|
-
logger$
|
|
5707
|
+
logger$2.log(` Using registry: ${registry.registryName}`);
|
|
6174
5708
|
} catch {
|
|
6175
|
-
logger$
|
|
5709
|
+
logger$2.log(" ⚠ Stored registry not found, clearing...");
|
|
6176
5710
|
registryId = void 0;
|
|
6177
5711
|
await require_credentials.storeDokployRegistryId("");
|
|
6178
5712
|
}
|
|
6179
5713
|
if (!registryId) {
|
|
6180
5714
|
const registries = await api.listRegistries();
|
|
6181
5715
|
if (registries.length === 0) if (dockerConfig.registry) {
|
|
6182
|
-
logger$
|
|
6183
|
-
logger$
|
|
5716
|
+
logger$2.log(" No registries found in Dokploy. Let's create one.");
|
|
5717
|
+
logger$2.log(` Registry URL: ${dockerConfig.registry}`);
|
|
6184
5718
|
const username = await prompt("Registry username: ");
|
|
6185
5719
|
const password = await prompt("Registry password/token: ", true);
|
|
6186
5720
|
const registry = await api.createRegistry("Default Registry", dockerConfig.registry, username, password);
|
|
6187
5721
|
registryId = registry.registryId;
|
|
6188
5722
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
6189
|
-
logger$
|
|
6190
|
-
} else logger$
|
|
5723
|
+
logger$2.log(` ✓ Registry created: ${registryId}`);
|
|
5724
|
+
} else logger$2.log(" ⚠ No registry configured. Set docker.registry in gkm.config.ts");
|
|
6191
5725
|
else {
|
|
6192
|
-
logger$
|
|
5726
|
+
logger$2.log(" Available registries:");
|
|
6193
5727
|
registries.forEach((reg, i) => {
|
|
6194
|
-
logger$
|
|
5728
|
+
logger$2.log(` ${i + 1}. ${reg.registryName} (${reg.registryUrl})`);
|
|
6195
5729
|
});
|
|
6196
|
-
if (dockerConfig.registry) logger$
|
|
5730
|
+
if (dockerConfig.registry) logger$2.log(` ${registries.length + 1}. Create new registry`);
|
|
6197
5731
|
const maxOption = dockerConfig.registry ? registries.length + 1 : registries.length;
|
|
6198
5732
|
const selection = await prompt(` Select registry (1-${maxOption}): `);
|
|
6199
5733
|
const index = parseInt(selection, 10) - 1;
|
|
6200
5734
|
if (index >= 0 && index < registries.length) {
|
|
6201
5735
|
registryId = registries[index].registryId;
|
|
6202
5736
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
6203
|
-
logger$
|
|
5737
|
+
logger$2.log(` ✓ Selected: ${registries[index].registryName}`);
|
|
6204
5738
|
} else if (dockerConfig.registry && index === registries.length) {
|
|
6205
|
-
logger$
|
|
6206
|
-
logger$
|
|
5739
|
+
logger$2.log(`\n Creating new registry...`);
|
|
5740
|
+
logger$2.log(` Registry URL: ${dockerConfig.registry}`);
|
|
6207
5741
|
const username = await prompt(" Registry username: ");
|
|
6208
5742
|
const password = await prompt(" Registry password/token: ", true);
|
|
6209
5743
|
const registry = await api.createRegistry(dockerConfig.registry.replace(/^https?:\/\//, ""), dockerConfig.registry, username, password);
|
|
6210
5744
|
registryId = registry.registryId;
|
|
6211
5745
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
6212
|
-
logger$
|
|
6213
|
-
} else logger$
|
|
5746
|
+
logger$2.log(` ✓ Registry created: ${registryId}`);
|
|
5747
|
+
} else logger$2.log(" ⚠ Invalid selection, skipping registry setup");
|
|
6214
5748
|
}
|
|
6215
5749
|
}
|
|
6216
5750
|
const dokployConfig = {
|
|
@@ -6220,10 +5754,10 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
6220
5754
|
registryId: registryId ?? void 0
|
|
6221
5755
|
};
|
|
6222
5756
|
await updateConfig(dokployConfig);
|
|
6223
|
-
logger$
|
|
6224
|
-
logger$
|
|
6225
|
-
logger$
|
|
6226
|
-
if (registryId) logger$
|
|
5757
|
+
logger$2.log("\n✅ Dokploy setup complete!");
|
|
5758
|
+
logger$2.log(` Project: ${project.projectId}`);
|
|
5759
|
+
logger$2.log(` Application: ${applicationId}`);
|
|
5760
|
+
if (registryId) logger$2.log(` Registry: ${registryId}`);
|
|
6227
5761
|
const provisionResult = await provisionServices(api, project.projectId, environmentId, dockerConfig.appName, services, void 0);
|
|
6228
5762
|
return {
|
|
6229
5763
|
config: dokployConfig,
|
|
@@ -6254,45 +5788,45 @@ function generateTag(stage) {
|
|
|
6254
5788
|
async function workspaceDeployCommand(workspace, options) {
|
|
6255
5789
|
const { provider, stage, tag, apps: selectedApps } = options;
|
|
6256
5790
|
if (provider !== "dokploy") throw new Error(`Workspace deployment only supports Dokploy. Got: ${provider}`);
|
|
6257
|
-
logger$
|
|
6258
|
-
logger$
|
|
5791
|
+
logger$2.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
|
|
5792
|
+
logger$2.log(` Stage: ${stage}`);
|
|
6259
5793
|
const imageTag = tag ?? generateTag(stage);
|
|
6260
|
-
logger$
|
|
5794
|
+
logger$2.log(` Tag: ${imageTag}`);
|
|
6261
5795
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
6262
5796
|
let appsToDeployNames = buildOrder;
|
|
6263
5797
|
if (selectedApps && selectedApps.length > 0) {
|
|
6264
5798
|
const invalidApps = selectedApps.filter((name$1) => !workspace.apps[name$1]);
|
|
6265
5799
|
if (invalidApps.length > 0) throw new Error(`Unknown apps: ${invalidApps.join(", ")}\nAvailable apps: ${Object.keys(workspace.apps).join(", ")}`);
|
|
6266
5800
|
appsToDeployNames = buildOrder.filter((name$1) => selectedApps.includes(name$1));
|
|
6267
|
-
logger$
|
|
6268
|
-
} else logger$
|
|
5801
|
+
logger$2.log(` Deploying apps: ${appsToDeployNames.join(", ")}`);
|
|
5802
|
+
} else logger$2.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
|
|
6269
5803
|
const dokployApps = appsToDeployNames.filter((name$1) => {
|
|
6270
5804
|
const app = workspace.apps[name$1];
|
|
6271
5805
|
const target = app.resolvedDeployTarget;
|
|
6272
5806
|
if (!require_workspace.isDeployTargetSupported(target)) {
|
|
6273
|
-
logger$
|
|
5807
|
+
logger$2.log(` ⚠️ Skipping ${name$1}: ${require_workspace.getDeployTargetError(target, name$1)}`);
|
|
6274
5808
|
return false;
|
|
6275
5809
|
}
|
|
6276
5810
|
return true;
|
|
6277
5811
|
});
|
|
6278
5812
|
if (dokployApps.length === 0) throw new Error("No apps to deploy. All selected apps have unsupported deploy targets.");
|
|
6279
5813
|
appsToDeployNames = dokployApps;
|
|
6280
|
-
logger$
|
|
5814
|
+
logger$2.log("\n🔐 Loading secrets and analyzing environment requirements...");
|
|
6281
5815
|
const stageSecrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
6282
5816
|
if (!stageSecrets) {
|
|
6283
|
-
logger$
|
|
6284
|
-
logger$
|
|
5817
|
+
logger$2.log(` ⚠️ No secrets found for stage "${stage}"`);
|
|
5818
|
+
logger$2.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
|
|
6285
5819
|
}
|
|
6286
5820
|
const sniffedApps = await sniffAllApps(workspace.apps, workspace.root);
|
|
6287
5821
|
const encryptedSecrets = stageSecrets ? prepareSecretsForAllApps(stageSecrets, sniffedApps) : /* @__PURE__ */ new Map();
|
|
6288
5822
|
if (stageSecrets) {
|
|
6289
5823
|
const report = generateSecretsReport(encryptedSecrets, sniffedApps);
|
|
6290
|
-
if (report.appsWithSecrets.length > 0) logger$
|
|
6291
|
-
if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$
|
|
5824
|
+
if (report.appsWithSecrets.length > 0) logger$2.log(` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(", ")}`);
|
|
5825
|
+
if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$2.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(", ")}`);
|
|
6292
5826
|
}
|
|
6293
5827
|
let creds = await require_credentials.getDokployCredentials();
|
|
6294
5828
|
if (!creds) {
|
|
6295
|
-
logger$
|
|
5829
|
+
logger$2.log("\n📋 Dokploy credentials not found. Let's set them up.");
|
|
6296
5830
|
const endpoint = await prompt("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
6297
5831
|
const normalizedEndpoint = endpoint.replace(/\/$/, "");
|
|
6298
5832
|
try {
|
|
@@ -6300,9 +5834,9 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6300
5834
|
} catch {
|
|
6301
5835
|
throw new Error("Invalid URL format");
|
|
6302
5836
|
}
|
|
6303
|
-
logger$
|
|
5837
|
+
logger$2.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
|
|
6304
5838
|
const token = await prompt("API Token: ", true);
|
|
6305
|
-
logger$
|
|
5839
|
+
logger$2.log("\nValidating credentials...");
|
|
6306
5840
|
const isValid = await validateDokployToken(normalizedEndpoint, token);
|
|
6307
5841
|
if (!isValid) throw new Error("Invalid credentials. Please check your token.");
|
|
6308
5842
|
await require_credentials.storeDokployCredentials(token, normalizedEndpoint);
|
|
@@ -6310,43 +5844,43 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6310
5844
|
token,
|
|
6311
5845
|
endpoint: normalizedEndpoint
|
|
6312
5846
|
};
|
|
6313
|
-
logger$
|
|
5847
|
+
logger$2.log("✓ Credentials saved");
|
|
6314
5848
|
}
|
|
6315
5849
|
const api = new require_dokploy_api.DokployApi({
|
|
6316
5850
|
baseUrl: creds.endpoint,
|
|
6317
5851
|
token: creds.token
|
|
6318
5852
|
});
|
|
6319
|
-
logger$
|
|
5853
|
+
logger$2.log("\n📁 Setting up Dokploy project...");
|
|
6320
5854
|
const projectName = workspace.name;
|
|
6321
5855
|
const projects = await api.listProjects();
|
|
6322
5856
|
let project = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
6323
5857
|
let environmentId;
|
|
6324
5858
|
if (project) {
|
|
6325
|
-
logger$
|
|
5859
|
+
logger$2.log(` Found existing project: ${project.name}`);
|
|
6326
5860
|
const projectDetails = await api.getProject(project.projectId);
|
|
6327
5861
|
const environments = projectDetails.environments ?? [];
|
|
6328
5862
|
const matchingEnv = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
6329
5863
|
if (matchingEnv) {
|
|
6330
5864
|
environmentId = matchingEnv.environmentId;
|
|
6331
|
-
logger$
|
|
5865
|
+
logger$2.log(` Using environment: ${matchingEnv.name}`);
|
|
6332
5866
|
} else {
|
|
6333
|
-
logger$
|
|
5867
|
+
logger$2.log(` Creating "${stage}" environment...`);
|
|
6334
5868
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
6335
5869
|
environmentId = env.environmentId;
|
|
6336
|
-
logger$
|
|
5870
|
+
logger$2.log(` ✓ Created environment: ${stage}`);
|
|
6337
5871
|
}
|
|
6338
5872
|
} else {
|
|
6339
|
-
logger$
|
|
5873
|
+
logger$2.log(` Creating project: ${projectName}`);
|
|
6340
5874
|
const result = await api.createProject(projectName);
|
|
6341
5875
|
project = result.project;
|
|
6342
5876
|
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
6343
|
-
logger$
|
|
5877
|
+
logger$2.log(` Creating "${stage}" environment...`);
|
|
6344
5878
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
6345
5879
|
environmentId = env.environmentId;
|
|
6346
5880
|
} else environmentId = result.environment.environmentId;
|
|
6347
|
-
logger$
|
|
5881
|
+
logger$2.log(` ✓ Created project: ${project.projectId}`);
|
|
6348
5882
|
}
|
|
6349
|
-
logger$
|
|
5883
|
+
logger$2.log("\n📋 Loading deploy state...");
|
|
6350
5884
|
const stateProvider = await createStateProvider({
|
|
6351
5885
|
config: workspace.state,
|
|
6352
5886
|
workspaceRoot: workspace.root,
|
|
@@ -6354,27 +5888,27 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6354
5888
|
});
|
|
6355
5889
|
let state = await stateProvider.read(stage);
|
|
6356
5890
|
if (state) {
|
|
6357
|
-
logger$
|
|
5891
|
+
logger$2.log(` Found existing state for stage "${stage}"`);
|
|
6358
5892
|
if (state.projectId !== project.projectId) {
|
|
6359
|
-
logger$
|
|
5893
|
+
logger$2.log(` ⚠ Project ID changed, updating state`);
|
|
6360
5894
|
state.projectId = project.projectId;
|
|
6361
5895
|
}
|
|
6362
5896
|
if (state.environmentId !== environmentId) {
|
|
6363
|
-
logger$
|
|
5897
|
+
logger$2.log(` ⚠ Environment ID changed, updating state`);
|
|
6364
5898
|
state.environmentId = environmentId;
|
|
6365
5899
|
}
|
|
6366
5900
|
} else {
|
|
6367
|
-
logger$
|
|
5901
|
+
logger$2.log(` Creating new state for stage "${stage}"`);
|
|
6368
5902
|
state = createEmptyState(stage, project.projectId, environmentId);
|
|
6369
5903
|
}
|
|
6370
|
-
logger$
|
|
5904
|
+
logger$2.log("\n🐳 Checking registry...");
|
|
6371
5905
|
let registryId = await require_credentials.getDokployRegistryId();
|
|
6372
5906
|
const registry = workspace.deploy.dokploy?.registry;
|
|
6373
5907
|
if (registryId) try {
|
|
6374
5908
|
const reg = await api.getRegistry(registryId);
|
|
6375
|
-
logger$
|
|
5909
|
+
logger$2.log(` Using registry: ${reg.registryName}`);
|
|
6376
5910
|
} catch {
|
|
6377
|
-
logger$
|
|
5911
|
+
logger$2.log(" ⚠ Stored registry not found, clearing...");
|
|
6378
5912
|
registryId = void 0;
|
|
6379
5913
|
await require_credentials.storeDokployRegistryId("");
|
|
6380
5914
|
}
|
|
@@ -6383,17 +5917,17 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6383
5917
|
if (registries.length > 0) {
|
|
6384
5918
|
registryId = registries[0].registryId;
|
|
6385
5919
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
6386
|
-
logger$
|
|
5920
|
+
logger$2.log(` Using registry: ${registries[0].registryName}`);
|
|
6387
5921
|
} else if (registry) {
|
|
6388
|
-
logger$
|
|
6389
|
-
logger$
|
|
5922
|
+
logger$2.log(" No registries found in Dokploy. Let's create one.");
|
|
5923
|
+
logger$2.log(` Registry URL: ${registry}`);
|
|
6390
5924
|
const username = await prompt("Registry username: ");
|
|
6391
5925
|
const password = await prompt("Registry password/token: ", true);
|
|
6392
5926
|
const reg = await api.createRegistry("Default Registry", registry, username, password);
|
|
6393
5927
|
registryId = reg.registryId;
|
|
6394
5928
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
6395
|
-
logger$
|
|
6396
|
-
} else logger$
|
|
5929
|
+
logger$2.log(` ✓ Registry created: ${registryId}`);
|
|
5930
|
+
} else logger$2.log(" ⚠ No registry configured. Set deploy.dokploy.registry in workspace config");
|
|
6397
5931
|
}
|
|
6398
5932
|
const services = workspace.services;
|
|
6399
5933
|
const dockerServices = {
|
|
@@ -6403,7 +5937,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6403
5937
|
let provisionedPostgres = null;
|
|
6404
5938
|
let provisionedRedis = null;
|
|
6405
5939
|
if (dockerServices.postgres || dockerServices.redis) {
|
|
6406
|
-
logger$
|
|
5940
|
+
logger$2.log("\n🔧 Provisioning infrastructure services...");
|
|
6407
5941
|
const existingServiceIds = {
|
|
6408
5942
|
postgresId: getPostgresId(state),
|
|
6409
5943
|
redisId: getRedisId(state)
|
|
@@ -6421,7 +5955,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6421
5955
|
}
|
|
6422
5956
|
}
|
|
6423
5957
|
const skippedMobileApps = appsToDeployNames.filter((name$1) => workspace.apps[name$1].type === "mobile");
|
|
6424
|
-
if (skippedMobileApps.length > 0) logger$
|
|
5958
|
+
if (skippedMobileApps.length > 0) logger$2.log(`\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(", ")}`);
|
|
6425
5959
|
const backendApps = appsToDeployNames.filter((name$1) => workspace.apps[name$1].type === "backend");
|
|
6426
5960
|
const frontendApps = appsToDeployNames.filter((name$1) => workspace.apps[name$1].type === "web");
|
|
6427
5961
|
const perAppDbCredentials = /* @__PURE__ */ new Map();
|
|
@@ -6431,13 +5965,13 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6431
5965
|
return requirements?.requiredEnvVars.includes("DATABASE_URL");
|
|
6432
5966
|
});
|
|
6433
5967
|
if (appsNeedingDb.length > 0) {
|
|
6434
|
-
logger$
|
|
6435
|
-
logger$
|
|
5968
|
+
logger$2.log(`\n🔐 Setting up per-app database credentials...`);
|
|
5969
|
+
logger$2.log(` Apps needing DATABASE_URL: ${appsNeedingDb.join(", ")}`);
|
|
6436
5970
|
const existingCredentials = getAllAppCredentials(state);
|
|
6437
5971
|
const usersToCreate = [];
|
|
6438
5972
|
for (const appName of appsNeedingDb) {
|
|
6439
5973
|
let credentials = existingCredentials[appName];
|
|
6440
|
-
if (credentials) logger$
|
|
5974
|
+
if (credentials) logger$2.log(` ${appName}: Using existing credentials from state`);
|
|
6441
5975
|
else {
|
|
6442
5976
|
const password = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
6443
5977
|
credentials = {
|
|
@@ -6445,7 +5979,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6445
5979
|
dbPassword: password
|
|
6446
5980
|
};
|
|
6447
5981
|
setAppCredentials(state, appName, credentials);
|
|
6448
|
-
logger$
|
|
5982
|
+
logger$2.log(` ${appName}: Generated new credentials`);
|
|
6449
5983
|
}
|
|
6450
5984
|
perAppDbCredentials.set(appName, credentials);
|
|
6451
5985
|
usersToCreate.push({
|
|
@@ -6459,8 +5993,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6459
5993
|
}
|
|
6460
5994
|
}
|
|
6461
5995
|
if (workspace.deploy?.backups && provisionedPostgres) {
|
|
6462
|
-
logger$
|
|
6463
|
-
const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-
|
|
5996
|
+
logger$2.log("\n💾 Provisioning backup destination...");
|
|
5997
|
+
const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-B3HBN-2k.cjs"));
|
|
6464
5998
|
const backupState = await provisionBackupDestination({
|
|
6465
5999
|
api,
|
|
6466
6000
|
projectId: project.projectId,
|
|
@@ -6468,13 +6002,13 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6468
6002
|
stage,
|
|
6469
6003
|
config: workspace.deploy.backups,
|
|
6470
6004
|
existingState: getBackupState(state),
|
|
6471
|
-
logger: logger$
|
|
6005
|
+
logger: logger$2
|
|
6472
6006
|
});
|
|
6473
6007
|
setBackupState(state, backupState);
|
|
6474
6008
|
if (!backupState.postgresBackupId) {
|
|
6475
6009
|
const backupSchedule = workspace.deploy.backups.schedule ?? "0 2 * * *";
|
|
6476
6010
|
const backupRetention = workspace.deploy.backups.retention ?? 30;
|
|
6477
|
-
logger$
|
|
6011
|
+
logger$2.log(" Creating postgres backup schedule...");
|
|
6478
6012
|
const backup = await api.createPostgresBackup({
|
|
6479
6013
|
schedule: backupSchedule,
|
|
6480
6014
|
prefix: `${stage}/postgres`,
|
|
@@ -6485,8 +6019,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6485
6019
|
keepLatestCount: backupRetention
|
|
6486
6020
|
});
|
|
6487
6021
|
setPostgresBackupId(state, backup.backupId);
|
|
6488
|
-
logger$
|
|
6489
|
-
} else logger$
|
|
6022
|
+
logger$2.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
|
|
6023
|
+
} else logger$2.log(" ✓ Using existing postgres backup schedule");
|
|
6490
6024
|
}
|
|
6491
6025
|
const publicUrls = {};
|
|
6492
6026
|
const results = [];
|
|
@@ -6501,25 +6035,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6501
6035
|
frontendUrls.push(`https://${hostname}`);
|
|
6502
6036
|
}
|
|
6503
6037
|
if (backendApps.length > 0) {
|
|
6504
|
-
logger$
|
|
6038
|
+
logger$2.log("\n📦 PHASE 1: Deploying backend applications...");
|
|
6505
6039
|
for (const appName of backendApps) {
|
|
6506
6040
|
const app = workspace.apps[appName];
|
|
6507
|
-
logger$
|
|
6041
|
+
logger$2.log(`\n ⚙️ Deploying ${appName}...`);
|
|
6508
6042
|
try {
|
|
6509
6043
|
const dokployAppName = appName;
|
|
6510
6044
|
let application = null;
|
|
6511
6045
|
const cachedAppId = getApplicationId(state, appName);
|
|
6512
6046
|
if (cachedAppId) {
|
|
6513
|
-
logger$
|
|
6047
|
+
logger$2.log(` Using cached ID: ${cachedAppId}`);
|
|
6514
6048
|
application = await api.getApplication(cachedAppId);
|
|
6515
|
-
if (application) logger$
|
|
6516
|
-
else logger$
|
|
6049
|
+
if (application) logger$2.log(` ✓ Application found: ${application.applicationId}`);
|
|
6050
|
+
else logger$2.log(` ⚠ Cached ID invalid, will create new`);
|
|
6517
6051
|
}
|
|
6518
6052
|
if (!application) {
|
|
6519
6053
|
const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
|
|
6520
6054
|
application = result.application;
|
|
6521
|
-
if (result.created) logger$
|
|
6522
|
-
else logger$
|
|
6055
|
+
if (result.created) logger$2.log(` Created application: ${application.applicationId}`);
|
|
6056
|
+
else logger$2.log(` Found existing application: ${application.applicationId}`);
|
|
6523
6057
|
}
|
|
6524
6058
|
setApplicationId(state, appName, application.applicationId);
|
|
6525
6059
|
const appSecrets = encryptedSecrets.get(appName);
|
|
@@ -6527,11 +6061,11 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6527
6061
|
if (appSecrets && appSecrets.secretCount > 0) {
|
|
6528
6062
|
buildArgs.push(`GKM_ENCRYPTED_CREDENTIALS=${appSecrets.payload.encrypted}`);
|
|
6529
6063
|
buildArgs.push(`GKM_CREDENTIALS_IV=${appSecrets.payload.iv}`);
|
|
6530
|
-
logger$
|
|
6064
|
+
logger$2.log(` Encrypted ${appSecrets.secretCount} secrets`);
|
|
6531
6065
|
}
|
|
6532
6066
|
const imageName = `${workspace.name}-${appName}`;
|
|
6533
6067
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
6534
|
-
logger$
|
|
6068
|
+
logger$2.log(` Building Docker image: ${imageRef}`);
|
|
6535
6069
|
await deployDocker({
|
|
6536
6070
|
stage,
|
|
6537
6071
|
tag: imageTag,
|
|
@@ -6581,10 +6115,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6581
6115
|
const { valid, missing, resolved } = validateEnvVars(requiredVars, envContext);
|
|
6582
6116
|
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
6583
6117
|
const envVars = Object.entries(resolved).map(([key, value]) => `${key}=${value}`);
|
|
6584
|
-
if (Object.keys(resolved).length > 0) logger$
|
|
6118
|
+
if (Object.keys(resolved).length > 0) logger$2.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
|
|
6585
6119
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
6586
6120
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
6587
|
-
logger$
|
|
6121
|
+
logger$2.log(` Deploying to Dokploy...`);
|
|
6588
6122
|
await api.deployApplication(application.applicationId);
|
|
6589
6123
|
const existingDomains = await api.getDomainsByApplicationId(application.applicationId);
|
|
6590
6124
|
const existingDomain = existingDomains.find((d) => d.host === backendHost);
|
|
@@ -6592,7 +6126,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6592
6126
|
appHostnames.set(appName, backendHost);
|
|
6593
6127
|
appDomainIds.set(appName, existingDomain.domainId);
|
|
6594
6128
|
publicUrls[appName] = `https://${backendHost}`;
|
|
6595
|
-
logger$
|
|
6129
|
+
logger$2.log(` ✓ Domain: https://${backendHost} (existing)`);
|
|
6596
6130
|
} else try {
|
|
6597
6131
|
const domain = await api.createDomain({
|
|
6598
6132
|
host: backendHost,
|
|
@@ -6604,10 +6138,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6604
6138
|
appHostnames.set(appName, backendHost);
|
|
6605
6139
|
appDomainIds.set(appName, domain.domainId);
|
|
6606
6140
|
publicUrls[appName] = `https://${backendHost}`;
|
|
6607
|
-
logger$
|
|
6141
|
+
logger$2.log(` ✓ Domain: https://${backendHost} (created)`);
|
|
6608
6142
|
} catch (domainError) {
|
|
6609
6143
|
const message = domainError instanceof Error ? domainError.message : "Unknown error";
|
|
6610
|
-
logger$
|
|
6144
|
+
logger$2.log(` ⚠ Domain creation failed: ${message}`);
|
|
6611
6145
|
appHostnames.set(appName, backendHost);
|
|
6612
6146
|
publicUrls[appName] = `https://${backendHost}`;
|
|
6613
6147
|
}
|
|
@@ -6618,10 +6152,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6618
6152
|
applicationId: application.applicationId,
|
|
6619
6153
|
imageRef
|
|
6620
6154
|
});
|
|
6621
|
-
logger$
|
|
6155
|
+
logger$2.log(` ✓ ${appName} deployed successfully`);
|
|
6622
6156
|
} catch (error) {
|
|
6623
6157
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6624
|
-
logger$
|
|
6158
|
+
logger$2.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
6625
6159
|
results.push({
|
|
6626
6160
|
appName,
|
|
6627
6161
|
type: app.type,
|
|
@@ -6633,25 +6167,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6633
6167
|
}
|
|
6634
6168
|
}
|
|
6635
6169
|
if (frontendApps.length > 0) {
|
|
6636
|
-
logger$
|
|
6170
|
+
logger$2.log("\n🌐 PHASE 2: Deploying frontend applications...");
|
|
6637
6171
|
for (const appName of frontendApps) {
|
|
6638
6172
|
const app = workspace.apps[appName];
|
|
6639
|
-
logger$
|
|
6173
|
+
logger$2.log(`\n 🌐 Deploying ${appName}...`);
|
|
6640
6174
|
try {
|
|
6641
6175
|
const dokployAppName = appName;
|
|
6642
6176
|
let application = null;
|
|
6643
6177
|
const cachedAppId = getApplicationId(state, appName);
|
|
6644
6178
|
if (cachedAppId) {
|
|
6645
|
-
logger$
|
|
6179
|
+
logger$2.log(` Using cached ID: ${cachedAppId}`);
|
|
6646
6180
|
application = await api.getApplication(cachedAppId);
|
|
6647
|
-
if (application) logger$
|
|
6648
|
-
else logger$
|
|
6181
|
+
if (application) logger$2.log(` ✓ Application found: ${application.applicationId}`);
|
|
6182
|
+
else logger$2.log(` ⚠ Cached ID invalid, will create new`);
|
|
6649
6183
|
}
|
|
6650
6184
|
if (!application) {
|
|
6651
6185
|
const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
|
|
6652
6186
|
application = result.application;
|
|
6653
|
-
if (result.created) logger$
|
|
6654
|
-
else logger$
|
|
6187
|
+
if (result.created) logger$2.log(` Created application: ${application.applicationId}`);
|
|
6188
|
+
else logger$2.log(` Found existing application: ${application.applicationId}`);
|
|
6655
6189
|
}
|
|
6656
6190
|
setApplicationId(state, appName, application.applicationId);
|
|
6657
6191
|
const dependencyUrls = {};
|
|
@@ -6673,7 +6207,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6673
6207
|
const sniffedVars = sniffedApps.get(appName)?.requiredEnvVars ?? [];
|
|
6674
6208
|
const { valid, missing, resolved } = validateEnvVars(sniffedVars, envContext);
|
|
6675
6209
|
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
6676
|
-
if (Object.keys(resolved).length > 0) logger$
|
|
6210
|
+
if (Object.keys(resolved).length > 0) logger$2.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
|
|
6677
6211
|
const publicPrefix = require_workspace.getPublicEnvPrefix(app.framework);
|
|
6678
6212
|
const buildArgs = [];
|
|
6679
6213
|
const publicUrlArgNames = [];
|
|
@@ -6683,10 +6217,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6683
6217
|
publicUrlArgNames.push(key);
|
|
6684
6218
|
}
|
|
6685
6219
|
}
|
|
6686
|
-
if (buildArgs.length > 0) logger$
|
|
6220
|
+
if (buildArgs.length > 0) logger$2.log(` Build args: ${publicUrlArgNames.join(", ")}`);
|
|
6687
6221
|
const imageName = `${workspace.name}-${appName}`;
|
|
6688
6222
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
6689
|
-
logger$
|
|
6223
|
+
logger$2.log(` Building Docker image: ${imageRef}`);
|
|
6690
6224
|
await deployDocker({
|
|
6691
6225
|
stage,
|
|
6692
6226
|
tag: imageTag,
|
|
@@ -6707,7 +6241,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6707
6241
|
for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
|
|
6708
6242
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
6709
6243
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
6710
|
-
logger$
|
|
6244
|
+
logger$2.log(` Deploying to Dokploy...`);
|
|
6711
6245
|
await api.deployApplication(application.applicationId);
|
|
6712
6246
|
const existingFrontendDomains = await api.getDomainsByApplicationId(application.applicationId);
|
|
6713
6247
|
const existingFrontendDomain = existingFrontendDomains.find((d) => d.host === frontendHost);
|
|
@@ -6715,7 +6249,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6715
6249
|
appHostnames.set(appName, frontendHost);
|
|
6716
6250
|
appDomainIds.set(appName, existingFrontendDomain.domainId);
|
|
6717
6251
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6718
|
-
logger$
|
|
6252
|
+
logger$2.log(` ✓ Domain: https://${frontendHost} (existing)`);
|
|
6719
6253
|
} else try {
|
|
6720
6254
|
const domain = await api.createDomain({
|
|
6721
6255
|
host: frontendHost,
|
|
@@ -6727,10 +6261,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6727
6261
|
appHostnames.set(appName, frontendHost);
|
|
6728
6262
|
appDomainIds.set(appName, domain.domainId);
|
|
6729
6263
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6730
|
-
logger$
|
|
6264
|
+
logger$2.log(` ✓ Domain: https://${frontendHost} (created)`);
|
|
6731
6265
|
} catch (domainError) {
|
|
6732
6266
|
const message = domainError instanceof Error ? domainError.message : "Unknown error";
|
|
6733
|
-
logger$
|
|
6267
|
+
logger$2.log(` ⚠ Domain creation failed: ${message}`);
|
|
6734
6268
|
appHostnames.set(appName, frontendHost);
|
|
6735
6269
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6736
6270
|
}
|
|
@@ -6741,10 +6275,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6741
6275
|
applicationId: application.applicationId,
|
|
6742
6276
|
imageRef
|
|
6743
6277
|
});
|
|
6744
|
-
logger$
|
|
6278
|
+
logger$2.log(` ✓ ${appName} deployed successfully`);
|
|
6745
6279
|
} catch (error) {
|
|
6746
6280
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6747
|
-
logger$
|
|
6281
|
+
logger$2.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
6748
6282
|
results.push({
|
|
6749
6283
|
appName,
|
|
6750
6284
|
type: app.type,
|
|
@@ -6754,9 +6288,9 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6754
6288
|
}
|
|
6755
6289
|
}
|
|
6756
6290
|
}
|
|
6757
|
-
logger$
|
|
6291
|
+
logger$2.log("\n📋 Saving deploy state...");
|
|
6758
6292
|
await stateProvider.write(stage, state);
|
|
6759
|
-
logger$
|
|
6293
|
+
logger$2.log(" ✓ State saved");
|
|
6760
6294
|
const dnsConfig = workspace.deploy.dns;
|
|
6761
6295
|
if (dnsConfig && appHostnames.size > 0) {
|
|
6762
6296
|
const dnsResult = await orchestrateDns(appHostnames, dnsConfig, creds.endpoint);
|
|
@@ -6765,27 +6299,27 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6765
6299
|
await stateProvider.write(stage, state);
|
|
6766
6300
|
}
|
|
6767
6301
|
if (dnsResult?.success && appHostnames.size > 0) {
|
|
6768
|
-
logger$
|
|
6302
|
+
logger$2.log("\n🔒 Validating domains for SSL certificates...");
|
|
6769
6303
|
for (const [appName, hostname] of appHostnames) try {
|
|
6770
6304
|
const result = await api.validateDomain(hostname);
|
|
6771
|
-
if (result.isValid) logger$
|
|
6772
|
-
else logger$
|
|
6305
|
+
if (result.isValid) logger$2.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);
|
|
6306
|
+
else logger$2.log(` ⚠ ${appName}: ${hostname} not valid`);
|
|
6773
6307
|
} catch (validationError) {
|
|
6774
6308
|
const message = validationError instanceof Error ? validationError.message : "Unknown error";
|
|
6775
|
-
logger$
|
|
6309
|
+
logger$2.log(` ⚠ ${appName}: validation failed - ${message}`);
|
|
6776
6310
|
}
|
|
6777
6311
|
}
|
|
6778
6312
|
}
|
|
6779
6313
|
const successCount = results.filter((r) => r.success).length;
|
|
6780
6314
|
const failedCount = results.filter((r) => !r.success).length;
|
|
6781
|
-
logger$
|
|
6782
|
-
logger$
|
|
6783
|
-
logger$
|
|
6784
|
-
logger$
|
|
6785
|
-
if (failedCount > 0) logger$
|
|
6315
|
+
logger$2.log(`\n${"─".repeat(50)}`);
|
|
6316
|
+
logger$2.log(`\n✅ Workspace deployment complete!`);
|
|
6317
|
+
logger$2.log(` Project: ${project.projectId}`);
|
|
6318
|
+
logger$2.log(` Successful: ${successCount}`);
|
|
6319
|
+
if (failedCount > 0) logger$2.log(` Failed: ${failedCount}`);
|
|
6786
6320
|
if (Object.keys(publicUrls).length > 0) {
|
|
6787
|
-
logger$
|
|
6788
|
-
for (const [name$1, url] of Object.entries(publicUrls)) logger$
|
|
6321
|
+
logger$2.log("\n 📡 Deployed URLs:");
|
|
6322
|
+
for (const [name$1, url] of Object.entries(publicUrls)) logger$2.log(` ${name$1}: ${url}`);
|
|
6789
6323
|
}
|
|
6790
6324
|
return {
|
|
6791
6325
|
apps: results,
|
|
@@ -6801,14 +6335,14 @@ async function deployCommand(options) {
|
|
|
6801
6335
|
const { provider, stage, tag, skipPush, skipBuild } = options;
|
|
6802
6336
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
6803
6337
|
if (loadedConfig.type === "workspace") {
|
|
6804
|
-
logger$
|
|
6338
|
+
logger$2.log("📦 Detected workspace configuration");
|
|
6805
6339
|
return workspaceDeployCommand(loadedConfig.workspace, options);
|
|
6806
6340
|
}
|
|
6807
|
-
logger$
|
|
6808
|
-
logger$
|
|
6341
|
+
logger$2.log(`\n🚀 Deploying to ${provider}...`);
|
|
6342
|
+
logger$2.log(` Stage: ${stage}`);
|
|
6809
6343
|
const config = await require_config.loadConfig();
|
|
6810
6344
|
const imageTag = tag ?? generateTag(stage);
|
|
6811
|
-
logger$
|
|
6345
|
+
logger$2.log(` Tag: ${imageTag}`);
|
|
6812
6346
|
const dockerConfig = resolveDockerConfig(config);
|
|
6813
6347
|
const imageName = dockerConfig.imageName;
|
|
6814
6348
|
const registry = dockerConfig.registry;
|
|
@@ -6817,7 +6351,7 @@ async function deployCommand(options) {
|
|
|
6817
6351
|
let finalRegistry = registry;
|
|
6818
6352
|
if (provider === "dokploy") {
|
|
6819
6353
|
const composeServices = config.docker?.compose?.services;
|
|
6820
|
-
logger$
|
|
6354
|
+
logger$2.log(`\n🔍 Docker compose config: ${JSON.stringify(config.docker?.compose)}`);
|
|
6821
6355
|
const dockerServices = composeServices ? Array.isArray(composeServices) ? {
|
|
6822
6356
|
postgres: composeServices.includes("postgres"),
|
|
6823
6357
|
redis: composeServices.includes("redis"),
|
|
@@ -6834,7 +6368,7 @@ async function deployCommand(options) {
|
|
|
6834
6368
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
6835
6369
|
let secrets = await readStageSecrets$1(stage);
|
|
6836
6370
|
if (!secrets) {
|
|
6837
|
-
logger$
|
|
6371
|
+
logger$2.log(` Creating secrets file for stage "${stage}"...`);
|
|
6838
6372
|
secrets = initStageSecrets(stage);
|
|
6839
6373
|
}
|
|
6840
6374
|
let updated = false;
|
|
@@ -6849,12 +6383,12 @@ async function deployCommand(options) {
|
|
|
6849
6383
|
const urlKey = key;
|
|
6850
6384
|
if (!secrets.urls[urlKey]) {
|
|
6851
6385
|
secrets.urls[urlKey] = value;
|
|
6852
|
-
logger$
|
|
6386
|
+
logger$2.log(` Saved ${key} to secrets.urls`);
|
|
6853
6387
|
updated = true;
|
|
6854
6388
|
}
|
|
6855
6389
|
} else if (!secrets.custom[key]) {
|
|
6856
6390
|
secrets.custom[key] = value;
|
|
6857
|
-
logger$
|
|
6391
|
+
logger$2.log(` Saved ${key} to secrets.custom`);
|
|
6858
6392
|
updated = true;
|
|
6859
6393
|
}
|
|
6860
6394
|
}
|
|
@@ -6863,14 +6397,14 @@ async function deployCommand(options) {
|
|
|
6863
6397
|
}
|
|
6864
6398
|
let masterKey;
|
|
6865
6399
|
if (!skipBuild) {
|
|
6866
|
-
logger$
|
|
6400
|
+
logger$2.log(`\n📦 Building for production...`);
|
|
6867
6401
|
const buildResult = await buildCommand({
|
|
6868
6402
|
provider: "server",
|
|
6869
6403
|
production: true,
|
|
6870
6404
|
stage
|
|
6871
6405
|
});
|
|
6872
6406
|
masterKey = buildResult.masterKey;
|
|
6873
|
-
} else logger$
|
|
6407
|
+
} else logger$2.log(`\n⏭️ Skipping build (--skip-build)`);
|
|
6874
6408
|
let result;
|
|
6875
6409
|
switch (provider) {
|
|
6876
6410
|
case "docker": {
|
|
@@ -6906,8 +6440,8 @@ async function deployCommand(options) {
|
|
|
6906
6440
|
break;
|
|
6907
6441
|
}
|
|
6908
6442
|
case "aws-lambda": {
|
|
6909
|
-
logger$
|
|
6910
|
-
logger$
|
|
6443
|
+
logger$2.log("\n⚠️ AWS Lambda deployment is not yet implemented.");
|
|
6444
|
+
logger$2.log(" Use SST or AWS CDK for Lambda deployments.");
|
|
6911
6445
|
result = {
|
|
6912
6446
|
imageRef,
|
|
6913
6447
|
masterKey
|
|
@@ -6916,7 +6450,7 @@ async function deployCommand(options) {
|
|
|
6916
6450
|
}
|
|
6917
6451
|
default: throw new Error(`Unknown deploy provider: ${provider}\nSupported providers: docker, dokploy, aws-lambda`);
|
|
6918
6452
|
}
|
|
6919
|
-
logger$
|
|
6453
|
+
logger$2.log("\n✅ Deployment complete!");
|
|
6920
6454
|
return result;
|
|
6921
6455
|
}
|
|
6922
6456
|
|
|
@@ -7113,22 +6647,22 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
7113
6647
|
"@geekmidas/audit": "~2.0.1",
|
|
7114
6648
|
"@geekmidas/auth": "~2.0.1",
|
|
7115
6649
|
"@geekmidas/cache": "~1.1.1",
|
|
7116
|
-
"@geekmidas/client": "~
|
|
6650
|
+
"@geekmidas/client": "~6.0.0",
|
|
7117
6651
|
"@geekmidas/cloud": "~1.0.1",
|
|
7118
|
-
"@geekmidas/constructs": "~
|
|
6652
|
+
"@geekmidas/constructs": "~4.0.0",
|
|
7119
6653
|
"@geekmidas/db": "~1.0.2",
|
|
7120
6654
|
"@geekmidas/emailkit": "~1.0.1",
|
|
7121
6655
|
"@geekmidas/envkit": "~1.0.7",
|
|
7122
6656
|
"@geekmidas/errors": "~1.0.1",
|
|
7123
6657
|
"@geekmidas/events": "~1.1.4",
|
|
7124
6658
|
"@geekmidas/logger": "~1.0.2",
|
|
7125
|
-
"@geekmidas/rate-limit": "~
|
|
6659
|
+
"@geekmidas/rate-limit": "~3.0.0",
|
|
7126
6660
|
"@geekmidas/schema": "~1.0.2",
|
|
7127
|
-
"@geekmidas/services": "~1.
|
|
6661
|
+
"@geekmidas/services": "~1.1.1",
|
|
7128
6662
|
"@geekmidas/storage": "~2.0.3",
|
|
7129
6663
|
"@geekmidas/studio": "~1.0.1",
|
|
7130
6664
|
"@geekmidas/telescope": "~1.0.1",
|
|
7131
|
-
"@geekmidas/testkit": "~
|
|
6665
|
+
"@geekmidas/testkit": "~2.0.0",
|
|
7132
6666
|
"@geekmidas/cli": CLI_VERSION
|
|
7133
6667
|
};
|
|
7134
6668
|
|
|
@@ -12868,7 +12402,7 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
12868
12402
|
|
|
12869
12403
|
//#endregion
|
|
12870
12404
|
//#region src/secrets/index.ts
|
|
12871
|
-
const logger$
|
|
12405
|
+
const logger$1 = console;
|
|
12872
12406
|
/**
|
|
12873
12407
|
* Extract service names from compose config.
|
|
12874
12408
|
*/
|
|
@@ -12884,12 +12418,12 @@ function getServicesFromConfig(services) {
|
|
|
12884
12418
|
async function secretsInitCommand(options) {
|
|
12885
12419
|
const { stage, force } = options;
|
|
12886
12420
|
if (!force && require_storage.secretsExist(stage)) {
|
|
12887
|
-
logger$
|
|
12421
|
+
logger$1.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
|
|
12888
12422
|
process.exit(1);
|
|
12889
12423
|
}
|
|
12890
12424
|
const config = await require_config.loadConfig();
|
|
12891
12425
|
const services = getServicesFromConfig(config.docker?.compose?.services);
|
|
12892
|
-
if (services.length === 0) logger$
|
|
12426
|
+
if (services.length === 0) logger$1.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
|
|
12893
12427
|
let projectName;
|
|
12894
12428
|
let workspaceSecrets;
|
|
12895
12429
|
try {
|
|
@@ -12898,23 +12432,23 @@ async function secretsInitCommand(options) {
|
|
|
12898
12432
|
const isMultiApp = Object.keys(loaded.workspace.apps).length > 1;
|
|
12899
12433
|
if (isMultiApp) {
|
|
12900
12434
|
workspaceSecrets = require_fullstack_secrets.generateFullstackCustomSecrets(loaded.workspace);
|
|
12901
|
-
logger$
|
|
12435
|
+
logger$1.log(" Detected workspace mode — generating per-app secrets");
|
|
12902
12436
|
}
|
|
12903
12437
|
} catch {}
|
|
12904
12438
|
const secrets = require_fullstack_secrets.createStageSecrets(stage, services, { projectName });
|
|
12905
12439
|
if (workspaceSecrets) secrets.custom = workspaceSecrets;
|
|
12906
12440
|
await require_storage.writeStageSecrets(secrets);
|
|
12907
|
-
logger$
|
|
12908
|
-
logger$
|
|
12909
|
-
logger$
|
|
12910
|
-
for (const service of services) logger$
|
|
12911
|
-
if (secrets.urls.DATABASE_URL) logger$
|
|
12912
|
-
if (secrets.urls.REDIS_URL) logger$
|
|
12913
|
-
if (secrets.urls.RABBITMQ_URL) logger$
|
|
12914
|
-
if (secrets.urls.STORAGE_ENDPOINT) logger$
|
|
12915
|
-
if (Object.keys(secrets.custom).length > 0) logger$
|
|
12916
|
-
logger$
|
|
12917
|
-
logger$
|
|
12441
|
+
logger$1.log(`\n✓ Secrets initialized for stage "${stage}"`);
|
|
12442
|
+
logger$1.log(` Location: .gkm/secrets/${stage}.json`);
|
|
12443
|
+
logger$1.log("\n Generated credentials for:");
|
|
12444
|
+
for (const service of services) logger$1.log(` - ${service}`);
|
|
12445
|
+
if (secrets.urls.DATABASE_URL) logger$1.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
12446
|
+
if (secrets.urls.REDIS_URL) logger$1.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
12447
|
+
if (secrets.urls.RABBITMQ_URL) logger$1.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
12448
|
+
if (secrets.urls.STORAGE_ENDPOINT) logger$1.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
|
|
12449
|
+
if (Object.keys(secrets.custom).length > 0) logger$1.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
|
|
12450
|
+
logger$1.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
|
|
12451
|
+
logger$1.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
|
|
12918
12452
|
}
|
|
12919
12453
|
/**
|
|
12920
12454
|
* Read all data from stdin.
|
|
@@ -12933,21 +12467,21 @@ async function secretsSetCommand(key, value, options) {
|
|
|
12933
12467
|
let secretValue = value;
|
|
12934
12468
|
if (!secretValue) {
|
|
12935
12469
|
if (process.stdin.isTTY) {
|
|
12936
|
-
logger$
|
|
12937
|
-
logger$
|
|
12470
|
+
logger$1.error("No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>");
|
|
12471
|
+
logger$1.error("Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>");
|
|
12938
12472
|
process.exit(1);
|
|
12939
12473
|
}
|
|
12940
12474
|
secretValue = await readStdin();
|
|
12941
12475
|
if (!secretValue) {
|
|
12942
|
-
logger$
|
|
12476
|
+
logger$1.error("No value received from stdin");
|
|
12943
12477
|
process.exit(1);
|
|
12944
12478
|
}
|
|
12945
12479
|
}
|
|
12946
12480
|
try {
|
|
12947
12481
|
await require_storage.setCustomSecret(stage, key, secretValue);
|
|
12948
|
-
logger$
|
|
12482
|
+
logger$1.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
|
|
12949
12483
|
} catch (error) {
|
|
12950
|
-
logger$
|
|
12484
|
+
logger$1.error(error instanceof Error ? error.message : "Failed to set secret");
|
|
12951
12485
|
process.exit(1);
|
|
12952
12486
|
}
|
|
12953
12487
|
}
|
|
@@ -12958,34 +12492,34 @@ async function secretsShowCommand(options) {
|
|
|
12958
12492
|
const { stage, reveal } = options;
|
|
12959
12493
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
12960
12494
|
if (!secrets) {
|
|
12961
|
-
logger$
|
|
12495
|
+
logger$1.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
12962
12496
|
process.exit(1);
|
|
12963
12497
|
}
|
|
12964
|
-
logger$
|
|
12965
|
-
logger$
|
|
12966
|
-
logger$
|
|
12967
|
-
logger$
|
|
12498
|
+
logger$1.log(`\nSecrets for stage "${stage}":`);
|
|
12499
|
+
logger$1.log(` Created: ${secrets.createdAt}`);
|
|
12500
|
+
logger$1.log(` Updated: ${secrets.updatedAt}`);
|
|
12501
|
+
logger$1.log("\nService Credentials:");
|
|
12968
12502
|
for (const [service, creds] of Object.entries(secrets.services)) if (creds) {
|
|
12969
|
-
logger$
|
|
12970
|
-
logger$
|
|
12971
|
-
logger$
|
|
12972
|
-
logger$
|
|
12973
|
-
logger$
|
|
12974
|
-
if (creds.database) logger$
|
|
12975
|
-
if (creds.vhost) logger$
|
|
12976
|
-
if (creds.bucket) logger$
|
|
12977
|
-
}
|
|
12978
|
-
logger$
|
|
12979
|
-
if (secrets.urls.DATABASE_URL) logger$
|
|
12980
|
-
if (secrets.urls.REDIS_URL) logger$
|
|
12981
|
-
if (secrets.urls.RABBITMQ_URL) logger$
|
|
12982
|
-
if (secrets.urls.STORAGE_ENDPOINT) logger$
|
|
12503
|
+
logger$1.log(`\n ${service}:`);
|
|
12504
|
+
logger$1.log(` host: ${creds.host}`);
|
|
12505
|
+
logger$1.log(` port: ${creds.port}`);
|
|
12506
|
+
logger$1.log(` username: ${creds.username}`);
|
|
12507
|
+
logger$1.log(` password: ${reveal ? creds.password : require_storage.maskPassword(creds.password)}`);
|
|
12508
|
+
if (creds.database) logger$1.log(` database: ${creds.database}`);
|
|
12509
|
+
if (creds.vhost) logger$1.log(` vhost: ${creds.vhost}`);
|
|
12510
|
+
if (creds.bucket) logger$1.log(` bucket: ${creds.bucket}`);
|
|
12511
|
+
}
|
|
12512
|
+
logger$1.log("\nConnection URLs:");
|
|
12513
|
+
if (secrets.urls.DATABASE_URL) logger$1.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
12514
|
+
if (secrets.urls.REDIS_URL) logger$1.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
|
|
12515
|
+
if (secrets.urls.RABBITMQ_URL) logger$1.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
12516
|
+
if (secrets.urls.STORAGE_ENDPOINT) logger$1.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
|
|
12983
12517
|
const customKeys = Object.keys(secrets.custom);
|
|
12984
12518
|
if (customKeys.length > 0) {
|
|
12985
|
-
logger$
|
|
12986
|
-
for (const [key, value] of Object.entries(secrets.custom)) logger$
|
|
12519
|
+
logger$1.log("\nCustom Secrets:");
|
|
12520
|
+
for (const [key, value] of Object.entries(secrets.custom)) logger$1.log(` ${key}: ${reveal ? value : require_storage.maskPassword(value)}`);
|
|
12987
12521
|
}
|
|
12988
|
-
if (!reveal) logger$
|
|
12522
|
+
if (!reveal) logger$1.log("\nUse --reveal to show actual values");
|
|
12989
12523
|
}
|
|
12990
12524
|
/**
|
|
12991
12525
|
* Rotate passwords for services.
|
|
@@ -12994,25 +12528,25 @@ async function secretsRotateCommand(options) {
|
|
|
12994
12528
|
const { stage, service } = options;
|
|
12995
12529
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
12996
12530
|
if (!secrets) {
|
|
12997
|
-
logger$
|
|
12531
|
+
logger$1.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
12998
12532
|
process.exit(1);
|
|
12999
12533
|
}
|
|
13000
12534
|
if (service) {
|
|
13001
12535
|
if (!secrets.services[service]) {
|
|
13002
|
-
logger$
|
|
12536
|
+
logger$1.error(`Service "${service}" not configured in stage "${stage}"`);
|
|
13003
12537
|
process.exit(1);
|
|
13004
12538
|
}
|
|
13005
12539
|
const updated = require_fullstack_secrets.rotateServicePassword(secrets, service);
|
|
13006
12540
|
await require_storage.writeStageSecrets(updated);
|
|
13007
|
-
logger$
|
|
12541
|
+
logger$1.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
|
|
13008
12542
|
} else {
|
|
13009
12543
|
let updated = secrets;
|
|
13010
12544
|
const services = Object.keys(secrets.services);
|
|
13011
12545
|
for (const svc of services) updated = require_fullstack_secrets.rotateServicePassword(updated, svc);
|
|
13012
12546
|
await require_storage.writeStageSecrets(updated);
|
|
13013
|
-
logger$
|
|
12547
|
+
logger$1.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
|
|
13014
12548
|
}
|
|
13015
|
-
logger$
|
|
12549
|
+
logger$1.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
|
|
13016
12550
|
}
|
|
13017
12551
|
/**
|
|
13018
12552
|
* Import secrets from a JSON file.
|
|
@@ -13020,7 +12554,7 @@ async function secretsRotateCommand(options) {
|
|
|
13020
12554
|
async function secretsImportCommand(file, options) {
|
|
13021
12555
|
const { stage, merge = true } = options;
|
|
13022
12556
|
if (!(0, node_fs.existsSync)(file)) {
|
|
13023
|
-
logger$
|
|
12557
|
+
logger$1.error(`File not found: ${file}`);
|
|
13024
12558
|
process.exit(1);
|
|
13025
12559
|
}
|
|
13026
12560
|
let importedSecrets;
|
|
@@ -13030,12 +12564,12 @@ async function secretsImportCommand(file, options) {
|
|
|
13030
12564
|
if (typeof importedSecrets !== "object" || importedSecrets === null) throw new Error("JSON must be an object");
|
|
13031
12565
|
for (const [key, value] of Object.entries(importedSecrets)) if (typeof value !== "string") throw new Error(`Value for "${key}" must be a string, got ${typeof value}`);
|
|
13032
12566
|
} catch (error) {
|
|
13033
|
-
logger$
|
|
12567
|
+
logger$1.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
|
|
13034
12568
|
process.exit(1);
|
|
13035
12569
|
}
|
|
13036
12570
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
13037
12571
|
if (!secrets) {
|
|
13038
|
-
logger$
|
|
12572
|
+
logger$1.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
13039
12573
|
process.exit(1);
|
|
13040
12574
|
}
|
|
13041
12575
|
const updatedCustom = merge ? {
|
|
@@ -13050,10 +12584,10 @@ async function secretsImportCommand(file, options) {
|
|
|
13050
12584
|
await require_storage.writeStageSecrets(updated);
|
|
13051
12585
|
const importedCount = Object.keys(importedSecrets).length;
|
|
13052
12586
|
const totalCount = Object.keys(updatedCustom).length;
|
|
13053
|
-
logger$
|
|
13054
|
-
if (merge && totalCount > importedCount) logger$
|
|
13055
|
-
logger$
|
|
13056
|
-
for (const key of Object.keys(importedSecrets)) logger$
|
|
12587
|
+
logger$1.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
|
|
12588
|
+
if (merge && totalCount > importedCount) logger$1.log(` Total custom secrets: ${totalCount}`);
|
|
12589
|
+
logger$1.log("\n Imported keys:");
|
|
12590
|
+
for (const key of Object.keys(importedSecrets)) logger$1.log(` - ${key}`);
|
|
13057
12591
|
}
|
|
13058
12592
|
/**
|
|
13059
12593
|
* Mask password in a URL for display.
|
|
@@ -13068,264 +12602,6 @@ function maskUrl(url) {
|
|
|
13068
12602
|
}
|
|
13069
12603
|
}
|
|
13070
12604
|
|
|
13071
|
-
//#endregion
|
|
13072
|
-
//#region src/setup/index.ts
|
|
13073
|
-
const logger$1 = console;
|
|
13074
|
-
/**
|
|
13075
|
-
* Setup development environment.
|
|
13076
|
-
*
|
|
13077
|
-
* Orchestrates:
|
|
13078
|
-
* 1. Load workspace config
|
|
13079
|
-
* 2. Resolve secrets (local → SSM → generate fresh)
|
|
13080
|
-
* 3. Write docker/.env from secrets
|
|
13081
|
-
* 4. Start Docker services
|
|
13082
|
-
*/
|
|
13083
|
-
async function setupCommand(options = {}) {
|
|
13084
|
-
const stage = options.stage ?? "development";
|
|
13085
|
-
logger$1.log("\n🔧 Setting up development environment...\n");
|
|
13086
|
-
let loadedConfig;
|
|
13087
|
-
try {
|
|
13088
|
-
loadedConfig = await require_config.loadWorkspaceConfig();
|
|
13089
|
-
} catch {
|
|
13090
|
-
logger$1.error("❌ No gkm.config.ts found. Run this command from a workspace root.");
|
|
13091
|
-
process.exit(1);
|
|
13092
|
-
}
|
|
13093
|
-
const { workspace } = loadedConfig;
|
|
13094
|
-
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
13095
|
-
logger$1.log(`📦 Workspace: ${workspace.name}`);
|
|
13096
|
-
logger$1.log(`📱 Apps: ${Object.keys(workspace.apps).join(", ")}`);
|
|
13097
|
-
logger$1.log(`🔑 Stage: ${stage}\n`);
|
|
13098
|
-
const secrets = await resolveSecrets(stage, workspace, options);
|
|
13099
|
-
if (!secrets) {
|
|
13100
|
-
logger$1.error("❌ Failed to resolve secrets. Exiting.");
|
|
13101
|
-
process.exit(1);
|
|
13102
|
-
}
|
|
13103
|
-
if (isMultiApp && workspace.services.db) {
|
|
13104
|
-
await require_fullstack_secrets.writeDockerEnvFromSecrets(secrets, workspace.root);
|
|
13105
|
-
logger$1.log("📄 Generated docker/.env with database passwords");
|
|
13106
|
-
}
|
|
13107
|
-
if (!options.skipDocker) {
|
|
13108
|
-
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
13109
|
-
if ((0, node_fs.existsSync)(composeFile)) {
|
|
13110
|
-
logger$1.log("");
|
|
13111
|
-
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
13112
|
-
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv);
|
|
13113
|
-
} else logger$1.log("⚠️ No docker-compose.yml found. Skipping Docker services.");
|
|
13114
|
-
}
|
|
13115
|
-
printSummary(workspace, stage);
|
|
13116
|
-
}
|
|
13117
|
-
/**
|
|
13118
|
-
* Resolve secrets with priority:
|
|
13119
|
-
* 1. Local secrets exist → use them (preserves manual additions)
|
|
13120
|
-
* 2. SSM configured and has secrets → pull and use
|
|
13121
|
-
* 3. Neither → generate fresh secrets
|
|
13122
|
-
*
|
|
13123
|
-
* --force skips checks 1 and 2 and always regenerates.
|
|
13124
|
-
*/
|
|
13125
|
-
async function resolveSecrets(stage, workspace, options) {
|
|
13126
|
-
if (options.force) {
|
|
13127
|
-
logger$1.log("🔐 Generating fresh secrets (--force)...");
|
|
13128
|
-
return generateFreshSecrets(stage, workspace, options);
|
|
13129
|
-
}
|
|
13130
|
-
if (require_storage.secretsExist(stage, workspace.root)) {
|
|
13131
|
-
logger$1.log("🔐 Using existing local secrets");
|
|
13132
|
-
const secrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
13133
|
-
if (secrets) {
|
|
13134
|
-
const reconciled = reconcileSecrets(secrets, workspace);
|
|
13135
|
-
if (reconciled) await require_storage.writeStageSecrets(reconciled, workspace.root);
|
|
13136
|
-
return reconciled ?? secrets;
|
|
13137
|
-
}
|
|
13138
|
-
}
|
|
13139
|
-
if (require_sync.isSSMConfigured(workspace)) {
|
|
13140
|
-
logger$1.log("☁️ Checking for remote secrets in SSM...");
|
|
13141
|
-
try {
|
|
13142
|
-
const remoteSecrets = await require_sync.pullSecrets(stage, workspace);
|
|
13143
|
-
if (remoteSecrets) {
|
|
13144
|
-
logger$1.log("✅ Pulled secrets from SSM");
|
|
13145
|
-
await require_storage.writeStageSecrets(remoteSecrets, workspace.root);
|
|
13146
|
-
return remoteSecrets;
|
|
13147
|
-
}
|
|
13148
|
-
logger$1.log(" No remote secrets found");
|
|
13149
|
-
} catch (error) {
|
|
13150
|
-
logger$1.warn(`⚠️ Failed to pull from SSM: ${error.message}`);
|
|
13151
|
-
}
|
|
13152
|
-
}
|
|
13153
|
-
logger$1.log("🔐 Generating fresh development secrets...");
|
|
13154
|
-
return generateFreshSecrets(stage, workspace, options);
|
|
13155
|
-
}
|
|
13156
|
-
/**
|
|
13157
|
-
* Reconcile existing secrets with expected workspace-derived keys.
|
|
13158
|
-
* Adds missing keys (e.g. BETTER_AUTH_*) without overwriting existing values.
|
|
13159
|
-
* Returns the updated secrets if changes were made, or null if no changes needed.
|
|
13160
|
-
* @internal Exported for testing
|
|
13161
|
-
*/
|
|
13162
|
-
function reconcileSecrets(secrets, workspace) {
|
|
13163
|
-
let changed = false;
|
|
13164
|
-
let result = { ...secrets };
|
|
13165
|
-
const serviceMap = [
|
|
13166
|
-
{
|
|
13167
|
-
key: "db",
|
|
13168
|
-
name: "postgres"
|
|
13169
|
-
},
|
|
13170
|
-
{
|
|
13171
|
-
key: "cache",
|
|
13172
|
-
name: "redis"
|
|
13173
|
-
},
|
|
13174
|
-
{
|
|
13175
|
-
key: "storage",
|
|
13176
|
-
name: "minio"
|
|
13177
|
-
},
|
|
13178
|
-
{
|
|
13179
|
-
key: "mail",
|
|
13180
|
-
name: "mailpit"
|
|
13181
|
-
}
|
|
13182
|
-
];
|
|
13183
|
-
for (const { key, name: name$1 } of serviceMap) if (workspace.services[key] && !result.services[name$1]) {
|
|
13184
|
-
const creds = require_fullstack_secrets.generateServiceCredentials(name$1);
|
|
13185
|
-
if (name$1 === "minio") {
|
|
13186
|
-
creds.bucket = workspace.name;
|
|
13187
|
-
creds.username = workspace.name;
|
|
13188
|
-
}
|
|
13189
|
-
result = {
|
|
13190
|
-
...result,
|
|
13191
|
-
services: {
|
|
13192
|
-
...result.services,
|
|
13193
|
-
[name$1]: creds
|
|
13194
|
-
}
|
|
13195
|
-
};
|
|
13196
|
-
result.urls = require_fullstack_secrets.generateConnectionUrls(result.services, result.eventsBackend);
|
|
13197
|
-
logger$1.log(` 🔄 Adding missing service credentials: ${name$1}`);
|
|
13198
|
-
changed = true;
|
|
13199
|
-
}
|
|
13200
|
-
if (result.services.postgres && !result.services.pgboss) {
|
|
13201
|
-
result = {
|
|
13202
|
-
...result,
|
|
13203
|
-
services: {
|
|
13204
|
-
...result.services,
|
|
13205
|
-
pgboss: {
|
|
13206
|
-
host: result.services.postgres.host,
|
|
13207
|
-
port: result.services.postgres.port,
|
|
13208
|
-
username: "pgboss",
|
|
13209
|
-
password: require_fullstack_secrets.generateSecurePassword(),
|
|
13210
|
-
database: result.services.postgres.database ?? "app"
|
|
13211
|
-
}
|
|
13212
|
-
}
|
|
13213
|
-
};
|
|
13214
|
-
result.urls = require_fullstack_secrets.generateConnectionUrls(result.services, result.eventsBackend);
|
|
13215
|
-
logger$1.log(" 🔄 Adding missing service credentials: pgboss");
|
|
13216
|
-
changed = true;
|
|
13217
|
-
}
|
|
13218
|
-
const eventsBackend = workspace.services.events;
|
|
13219
|
-
if (eventsBackend && result.eventsBackend !== eventsBackend) {
|
|
13220
|
-
result.eventsBackend = eventsBackend;
|
|
13221
|
-
if (eventsBackend === "sns" && !result.services.localstack) {
|
|
13222
|
-
result = {
|
|
13223
|
-
...result,
|
|
13224
|
-
services: {
|
|
13225
|
-
...result.services,
|
|
13226
|
-
localstack: require_fullstack_secrets.generateLocalStackCredentials()
|
|
13227
|
-
}
|
|
13228
|
-
};
|
|
13229
|
-
logger$1.log(" 🔄 Adding missing service credentials: localstack");
|
|
13230
|
-
changed = true;
|
|
13231
|
-
}
|
|
13232
|
-
if (eventsBackend === "rabbitmq" && !result.services.rabbitmq) {
|
|
13233
|
-
result = {
|
|
13234
|
-
...result,
|
|
13235
|
-
services: {
|
|
13236
|
-
...result.services,
|
|
13237
|
-
rabbitmq: require_fullstack_secrets.generateServiceCredentials("rabbitmq")
|
|
13238
|
-
}
|
|
13239
|
-
};
|
|
13240
|
-
logger$1.log(" 🔄 Adding missing service credentials: rabbitmq");
|
|
13241
|
-
changed = true;
|
|
13242
|
-
}
|
|
13243
|
-
result.urls = require_fullstack_secrets.generateConnectionUrls(result.services, eventsBackend);
|
|
13244
|
-
changed = true;
|
|
13245
|
-
}
|
|
13246
|
-
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
13247
|
-
if (isMultiApp) {
|
|
13248
|
-
const expected = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
|
|
13249
|
-
const missing = {};
|
|
13250
|
-
for (const [key, value] of Object.entries(expected)) if (!(key in result.custom)) missing[key] = value;
|
|
13251
|
-
if (Object.keys(missing).length > 0) {
|
|
13252
|
-
logger$1.log(` 🔄 Adding missing secrets: ${Object.keys(missing).join(", ")}`);
|
|
13253
|
-
result = {
|
|
13254
|
-
...result,
|
|
13255
|
-
custom: {
|
|
13256
|
-
...result.custom,
|
|
13257
|
-
...missing
|
|
13258
|
-
}
|
|
13259
|
-
};
|
|
13260
|
-
changed = true;
|
|
13261
|
-
}
|
|
13262
|
-
}
|
|
13263
|
-
if (!changed) return null;
|
|
13264
|
-
return {
|
|
13265
|
-
...result,
|
|
13266
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13267
|
-
};
|
|
13268
|
-
}
|
|
13269
|
-
/**
|
|
13270
|
-
* Generate fresh secrets for the workspace.
|
|
13271
|
-
*/
|
|
13272
|
-
async function generateFreshSecrets(stage, workspace, options) {
|
|
13273
|
-
const serviceNames = [];
|
|
13274
|
-
if (workspace.services.db) serviceNames.push("postgres");
|
|
13275
|
-
if (workspace.services.cache) serviceNames.push("redis");
|
|
13276
|
-
if (workspace.services.storage) serviceNames.push("minio");
|
|
13277
|
-
if (workspace.services.mail) serviceNames.push("mailpit");
|
|
13278
|
-
if (workspace.services.events === "sns") serviceNames.push("localstack");
|
|
13279
|
-
if (workspace.services.events === "rabbitmq") serviceNames.push("rabbitmq");
|
|
13280
|
-
const secrets = require_fullstack_secrets.createStageSecrets(stage, serviceNames, {
|
|
13281
|
-
projectName: workspace.name,
|
|
13282
|
-
eventsBackend: workspace.services.events
|
|
13283
|
-
});
|
|
13284
|
-
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
13285
|
-
if (isMultiApp) {
|
|
13286
|
-
const customSecrets = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
|
|
13287
|
-
secrets.custom = customSecrets;
|
|
13288
|
-
} else secrets.custom = {
|
|
13289
|
-
NODE_ENV: "development",
|
|
13290
|
-
PORT: "3000",
|
|
13291
|
-
LOG_LEVEL: "debug",
|
|
13292
|
-
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
13293
|
-
};
|
|
13294
|
-
await require_storage.writeStageSecrets(secrets, workspace.root);
|
|
13295
|
-
logger$1.log(` Secrets written to .gkm/secrets/${stage}.json`);
|
|
13296
|
-
if (require_sync.isSSMConfigured(workspace) && !options.yes) {
|
|
13297
|
-
const { shouldPush } = await (0, prompts.default)({
|
|
13298
|
-
type: "confirm",
|
|
13299
|
-
name: "shouldPush",
|
|
13300
|
-
message: "Push secrets to SSM for team sharing?",
|
|
13301
|
-
initial: true
|
|
13302
|
-
});
|
|
13303
|
-
if (shouldPush) try {
|
|
13304
|
-
await require_sync.pushSecrets(stage, workspace);
|
|
13305
|
-
logger$1.log("☁️ Secrets pushed to SSM");
|
|
13306
|
-
} catch (error) {
|
|
13307
|
-
logger$1.warn(`⚠️ Failed to push to SSM: ${error.message}`);
|
|
13308
|
-
}
|
|
13309
|
-
}
|
|
13310
|
-
return secrets;
|
|
13311
|
-
}
|
|
13312
|
-
/**
|
|
13313
|
-
* Print setup summary with next steps.
|
|
13314
|
-
*/
|
|
13315
|
-
function printSummary(workspace, stage) {
|
|
13316
|
-
logger$1.log(`\n${"─".repeat(50)}`);
|
|
13317
|
-
logger$1.log("\n✅ Development environment ready!\n");
|
|
13318
|
-
logger$1.log("📋 Apps:");
|
|
13319
|
-
for (const [name$1, app] of Object.entries(workspace.apps)) {
|
|
13320
|
-
const icon = app.type === "web" ? "🌐" : app.type === "mobile" ? "📱" : "🔧";
|
|
13321
|
-
logger$1.log(` ${icon} ${name$1} → http://localhost:${app.port}`);
|
|
13322
|
-
}
|
|
13323
|
-
logger$1.log("\n🚀 Next steps:");
|
|
13324
|
-
logger$1.log(" gkm dev # Start all apps");
|
|
13325
|
-
logger$1.log(` gkm secrets:show --stage ${stage} # View secrets`);
|
|
13326
|
-
logger$1.log("");
|
|
13327
|
-
}
|
|
13328
|
-
|
|
13329
12605
|
//#endregion
|
|
13330
12606
|
//#region src/test/index.ts
|
|
13331
12607
|
/**
|
|
@@ -13336,9 +12612,15 @@ async function testCommand(options = {}) {
|
|
|
13336
12612
|
const stage = options.stage ?? "development";
|
|
13337
12613
|
const cwd = process.cwd();
|
|
13338
12614
|
console.log(`\n🧪 Running tests with ${stage} environment...\n`);
|
|
13339
|
-
const
|
|
12615
|
+
const autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);
|
|
12616
|
+
if (autoSetup) {
|
|
12617
|
+
const { ensureStageSecrets } = await Promise.resolve().then(() => require("./setup-DLwhfyt-.cjs"));
|
|
12618
|
+
const generated = await ensureStageSecrets(stage, cwd);
|
|
12619
|
+
if (generated) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
|
|
12620
|
+
}
|
|
12621
|
+
const defaultEnv = require_setup.loadEnvFiles(".env");
|
|
13340
12622
|
if (defaultEnv.loaded.length > 0) console.log(` 📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
13341
|
-
const result = await prepareEntryCredentials({
|
|
12623
|
+
const result = await require_setup.prepareEntryCredentials({
|
|
13342
12624
|
stages: [stage],
|
|
13343
12625
|
startDocker: true,
|
|
13344
12626
|
secretsFileName: "test-secrets.json",
|
|
@@ -13360,7 +12642,7 @@ async function testCommand(options = {}) {
|
|
|
13360
12642
|
await (0, node_fs_promises.writeFile)(result.secretsJsonPath, JSON.stringify(finalCredentials, null, 2));
|
|
13361
12643
|
const gkmDir = (0, node_path.join)(cwd, ".gkm");
|
|
13362
12644
|
const preloadPath = (0, node_path.join)(gkmDir, "test-credentials-preload.ts");
|
|
13363
|
-
await createCredentialsPreload(preloadPath, result.secretsJsonPath);
|
|
12645
|
+
await require_setup.createCredentialsPreload(preloadPath, result.secretsJsonPath);
|
|
13364
12646
|
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
13365
12647
|
const tsxImport = "--import=tsx";
|
|
13366
12648
|
const preloadImport = `--import=${preloadPath}`;
|
|
@@ -13385,9 +12667,9 @@ async function testCommand(options = {}) {
|
|
|
13385
12667
|
NODE_OPTIONS: nodeOptions
|
|
13386
12668
|
}
|
|
13387
12669
|
});
|
|
13388
|
-
return new Promise((resolve$
|
|
12670
|
+
return new Promise((resolve$3, reject) => {
|
|
13389
12671
|
vitestProcess.on("close", (code) => {
|
|
13390
|
-
if (code === 0) resolve$
|
|
12672
|
+
if (code === 0) resolve$3();
|
|
13391
12673
|
else reject(new Error(`Tests failed with exit code ${code}`));
|
|
13392
12674
|
});
|
|
13393
12675
|
vitestProcess.on("error", (error) => {
|
|
@@ -13580,7 +12862,7 @@ program.command("setup").description("Setup development environment (secrets, Do
|
|
|
13580
12862
|
try {
|
|
13581
12863
|
const globalOptions = program.opts();
|
|
13582
12864
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
13583
|
-
await setupCommand(options);
|
|
12865
|
+
await require_setup.setupCommand(options);
|
|
13584
12866
|
} catch (error) {
|
|
13585
12867
|
console.error(formatError(error));
|
|
13586
12868
|
process.exit(1);
|
|
@@ -13648,7 +12930,7 @@ program.command("exec").description("Run a command with secrets injected into Cr
|
|
|
13648
12930
|
process.exit(1);
|
|
13649
12931
|
}
|
|
13650
12932
|
});
|
|
13651
|
-
program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from", "development").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
|
|
12933
|
+
program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from", "development").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
|
|
13652
12934
|
try {
|
|
13653
12935
|
const globalOptions = program.opts();
|
|
13654
12936
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -13790,8 +13072,8 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
13790
13072
|
const globalOptions = program.opts();
|
|
13791
13073
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
13792
13074
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
13793
|
-
const { pushSecrets
|
|
13794
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
13075
|
+
const { pushSecrets } = await Promise.resolve().then(() => require("./sync-BLA94rgC.cjs"));
|
|
13076
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BzqKJ2Rt.cjs"));
|
|
13795
13077
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
13796
13078
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
13797
13079
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
@@ -13803,7 +13085,7 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
13803
13085
|
for (const key of result.addedKeys) console.log(` + ${key}`);
|
|
13804
13086
|
}
|
|
13805
13087
|
}
|
|
13806
|
-
await pushSecrets
|
|
13088
|
+
await pushSecrets(options.stage, workspace);
|
|
13807
13089
|
console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
|
|
13808
13090
|
} catch (error) {
|
|
13809
13091
|
console.error(formatError(error));
|
|
@@ -13815,11 +13097,11 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
13815
13097
|
const globalOptions = program.opts();
|
|
13816
13098
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
13817
13099
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
13818
|
-
const { pullSecrets
|
|
13100
|
+
const { pullSecrets } = await Promise.resolve().then(() => require("./sync-BLA94rgC.cjs"));
|
|
13819
13101
|
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
13820
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
13102
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BzqKJ2Rt.cjs"));
|
|
13821
13103
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
13822
|
-
let secrets = await pullSecrets
|
|
13104
|
+
let secrets = await pullSecrets(options.stage, workspace);
|
|
13823
13105
|
if (!secrets) {
|
|
13824
13106
|
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
13825
13107
|
process.exit(1);
|
|
@@ -13842,7 +13124,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
13842
13124
|
const globalOptions = program.opts();
|
|
13843
13125
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
13844
13126
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
13845
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
13127
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BzqKJ2Rt.cjs"));
|
|
13846
13128
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
13847
13129
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
13848
13130
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|