@geekmidas/cli 1.7.0 → 1.9.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 +21 -0
- package/dist/{HostingerProvider-BiXdHjiq.cjs → HostingerProvider-CEsQbmpY.cjs} +1 -1
- package/dist/{HostingerProvider-BiXdHjiq.cjs.map → HostingerProvider-CEsQbmpY.cjs.map} +1 -1
- package/dist/{HostingerProvider-402UdK89.mjs → HostingerProvider-DkahM5AP.mjs} +1 -1
- package/dist/{HostingerProvider-402UdK89.mjs.map → HostingerProvider-DkahM5AP.mjs.map} +1 -1
- package/dist/{LocalStateProvider-BDm7ZqJo.mjs → LocalStateProvider-DXIwWb7k.mjs} +1 -1
- package/dist/{LocalStateProvider-BDm7ZqJo.mjs.map → LocalStateProvider-DXIwWb7k.mjs.map} +1 -1
- package/dist/{LocalStateProvider-CdspeSVL.cjs → LocalStateProvider-Roi202l7.cjs} +1 -1
- package/dist/{LocalStateProvider-CdspeSVL.cjs.map → LocalStateProvider-Roi202l7.cjs.map} +1 -1
- package/dist/{Route53Provider-kfJ77LmL.cjs → Route53Provider-BqXeHzuc.cjs} +1 -1
- package/dist/{Route53Provider-kfJ77LmL.cjs.map → Route53Provider-BqXeHzuc.cjs.map} +1 -1
- package/dist/{Route53Provider-DbBo7Uz5.mjs → Route53Provider-Ckq_n5Be.mjs} +1 -1
- package/dist/{Route53Provider-DbBo7Uz5.mjs.map → Route53Provider-Ckq_n5Be.mjs.map} +1 -1
- package/dist/{SSMStateProvider-DGrqYll0.cjs → SSMStateProvider-BReQA5re.cjs} +1 -1
- package/dist/{SSMStateProvider-DGrqYll0.cjs.map → SSMStateProvider-BReQA5re.cjs.map} +1 -1
- package/dist/{SSMStateProvider-DT0WV-E_.mjs → SSMStateProvider-wddd0_-d.mjs} +1 -1
- package/dist/{SSMStateProvider-DT0WV-E_.mjs.map → SSMStateProvider-wddd0_-d.mjs.map} +1 -1
- package/dist/{backup-provisioner-BIArpmTr.mjs → backup-provisioner-BAExdDtc.mjs} +1 -1
- package/dist/{backup-provisioner-BIArpmTr.mjs.map → backup-provisioner-BAExdDtc.mjs.map} +1 -1
- package/dist/{backup-provisioner-B5e-F6zX.cjs → backup-provisioner-C8VK63I-.cjs} +1 -1
- package/dist/{backup-provisioner-B5e-F6zX.cjs.map → backup-provisioner-C8VK63I-.cjs.map} +1 -1
- package/dist/{bundler-DgXsOSxc.mjs → bundler-BxHyDhdt.mjs} +1 -1
- package/dist/{bundler-DgXsOSxc.mjs.map → bundler-BxHyDhdt.mjs.map} +1 -1
- package/dist/{bundler-tHLLwYuU.cjs → bundler-CuMIfXw5.cjs} +1 -1
- package/dist/{bundler-tHLLwYuU.cjs.map → bundler-CuMIfXw5.cjs.map} +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/{index-C-KxSGGK.d.mts → index-BVNXOydm.d.mts} +2 -2
- package/dist/{index-C-KxSGGK.d.mts.map → index-BVNXOydm.d.mts.map} +1 -1
- package/dist/index.cjs +1019 -551
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1017 -549
- package/dist/index.mjs.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/sync-BOS0jKLn.cjs +93 -0
- package/dist/sync-BOS0jKLn.cjs.map +1 -0
- package/dist/sync-BnqNNc6O.mjs +3 -0
- package/dist/sync-BxFB34zW.cjs +4 -0
- package/dist/sync-CHfhmXF3.mjs +76 -0
- package/dist/sync-CHfhmXF3.mjs.map +1 -0
- package/dist/{types-CZg5iUgD.d.mts → types-eTlj5f2M.d.mts} +1 -1
- package/dist/{types-CZg5iUgD.d.mts.map → types-eTlj5f2M.d.mts.map} +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +4 -4
- package/src/dev/index.ts +1 -1
- package/src/generators/SubscriberGenerator.ts +1 -0
- package/src/index.ts +93 -0
- package/src/init/index.ts +4 -23
- package/src/init/utils.ts +103 -2
- package/src/init/versions.ts +1 -1
- package/src/secrets/index.ts +20 -1
- package/src/secrets/sync.ts +136 -0
- package/src/setup/fullstack-secrets.ts +121 -0
- package/src/setup/index.ts +212 -0
- package/src/test/__tests__/web.spec.ts +1 -1
- package/src/upgrade/__tests__/index.spec.ts +354 -0
- package/src/upgrade/index.ts +253 -0
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ const require_dokploy_api = require('./dokploy-api-DLgvEQlr.cjs');
|
|
|
9
9
|
const require_encryption = require('./encryption-BE0UOb8j.cjs');
|
|
10
10
|
const require_CachedStateProvider = require('./CachedStateProvider-D73dCqfH.cjs');
|
|
11
11
|
const require_openapi_react_query = require('./openapi-react-query-BeXvk-wa.cjs');
|
|
12
|
+
const require_sync = require('./sync-BOS0jKLn.cjs');
|
|
12
13
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
13
14
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
14
15
|
const commander = require_chunk.__toESM(require("commander"));
|
|
@@ -33,7 +34,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
33
34
|
|
|
34
35
|
//#region package.json
|
|
35
36
|
var name = "@geekmidas/cli";
|
|
36
|
-
var version = "1.
|
|
37
|
+
var version = "1.8.0";
|
|
37
38
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
38
39
|
var private$1 = false;
|
|
39
40
|
var type = "module";
|
|
@@ -131,7 +132,7 @@ var package_default = {
|
|
|
131
132
|
|
|
132
133
|
//#endregion
|
|
133
134
|
//#region src/auth/index.ts
|
|
134
|
-
const logger$
|
|
135
|
+
const logger$13 = console;
|
|
135
136
|
/**
|
|
136
137
|
* Validate Dokploy token by making a test API call
|
|
137
138
|
*/
|
|
@@ -199,36 +200,36 @@ async function prompt$1(message, hidden = false) {
|
|
|
199
200
|
async function loginCommand(options) {
|
|
200
201
|
const { service, token: providedToken, endpoint: providedEndpoint } = options;
|
|
201
202
|
if (service === "dokploy") {
|
|
202
|
-
logger$
|
|
203
|
+
logger$13.log("\n🔐 Logging in to Dokploy...\n");
|
|
203
204
|
let endpoint = providedEndpoint;
|
|
204
205
|
if (!endpoint) endpoint = await prompt$1("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
205
206
|
endpoint = endpoint.replace(/\/$/, "");
|
|
206
207
|
try {
|
|
207
208
|
new URL(endpoint);
|
|
208
209
|
} catch {
|
|
209
|
-
logger$
|
|
210
|
+
logger$13.error("Invalid URL format");
|
|
210
211
|
process.exit(1);
|
|
211
212
|
}
|
|
212
213
|
let token = providedToken;
|
|
213
214
|
if (!token) {
|
|
214
|
-
logger$
|
|
215
|
+
logger$13.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
|
|
215
216
|
token = await prompt$1("API Token: ", true);
|
|
216
217
|
}
|
|
217
218
|
if (!token) {
|
|
218
|
-
logger$
|
|
219
|
+
logger$13.error("Token is required");
|
|
219
220
|
process.exit(1);
|
|
220
221
|
}
|
|
221
|
-
logger$
|
|
222
|
+
logger$13.log("\nValidating credentials...");
|
|
222
223
|
const isValid = await validateDokployToken(endpoint, token);
|
|
223
224
|
if (!isValid) {
|
|
224
|
-
logger$
|
|
225
|
+
logger$13.error("\n✗ Invalid credentials. Please check your token and try again.");
|
|
225
226
|
process.exit(1);
|
|
226
227
|
}
|
|
227
228
|
await require_credentials.storeDokployCredentials(token, endpoint);
|
|
228
|
-
logger$
|
|
229
|
-
logger$
|
|
230
|
-
logger$
|
|
231
|
-
logger$
|
|
229
|
+
logger$13.log("\n✓ Successfully logged in to Dokploy!");
|
|
230
|
+
logger$13.log(` Endpoint: ${endpoint}`);
|
|
231
|
+
logger$13.log(` Credentials stored in: ${require_credentials.getCredentialsPath()}`);
|
|
232
|
+
logger$13.log("\nYou can now use deploy commands without setting DOKPLOY_API_TOKEN.");
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
/**
|
|
@@ -238,28 +239,28 @@ async function logoutCommand(options) {
|
|
|
238
239
|
const { service = "dokploy" } = options;
|
|
239
240
|
if (service === "all") {
|
|
240
241
|
const dokployRemoved = await require_credentials.removeDokployCredentials();
|
|
241
|
-
if (dokployRemoved) logger$
|
|
242
|
-
else logger$
|
|
242
|
+
if (dokployRemoved) logger$13.log("\n✓ Logged out from all services");
|
|
243
|
+
else logger$13.log("\nNo stored credentials found");
|
|
243
244
|
return;
|
|
244
245
|
}
|
|
245
246
|
if (service === "dokploy") {
|
|
246
247
|
const removed = await require_credentials.removeDokployCredentials();
|
|
247
|
-
if (removed) logger$
|
|
248
|
-
else logger$
|
|
248
|
+
if (removed) logger$13.log("\n✓ Logged out from Dokploy");
|
|
249
|
+
else logger$13.log("\nNo Dokploy credentials found");
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
/**
|
|
252
253
|
* Show current login status
|
|
253
254
|
*/
|
|
254
255
|
async function whoamiCommand() {
|
|
255
|
-
logger$
|
|
256
|
+
logger$13.log("\n📋 Current credentials:\n");
|
|
256
257
|
const dokploy = await require_credentials.getDokployCredentials();
|
|
257
258
|
if (dokploy) {
|
|
258
|
-
logger$
|
|
259
|
-
logger$
|
|
260
|
-
logger$
|
|
261
|
-
} else logger$
|
|
262
|
-
logger$
|
|
259
|
+
logger$13.log(" Dokploy:");
|
|
260
|
+
logger$13.log(` Endpoint: ${dokploy.endpoint}`);
|
|
261
|
+
logger$13.log(` Token: ${maskToken(dokploy.token)}`);
|
|
262
|
+
} else logger$13.log(" Dokploy: Not logged in");
|
|
263
|
+
logger$13.log(`\n Credentials file: ${require_credentials.getCredentialsPath()}`);
|
|
263
264
|
}
|
|
264
265
|
/**
|
|
265
266
|
* Mask a token for display
|
|
@@ -345,7 +346,7 @@ function isEnabled(config) {
|
|
|
345
346
|
var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
346
347
|
async build(context, constructs, outputDir, options) {
|
|
347
348
|
const provider = options?.provider || "aws-lambda";
|
|
348
|
-
const logger$
|
|
349
|
+
const logger$14 = console;
|
|
349
350
|
const cronInfos = [];
|
|
350
351
|
if (constructs.length === 0 || provider !== "aws-lambda") return cronInfos;
|
|
351
352
|
const cronsDir = (0, node_path.join)(outputDir, "crons");
|
|
@@ -360,7 +361,7 @@ var CronGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
360
361
|
memorySize: construct.memorySize,
|
|
361
362
|
environment: await construct.getEnvironment()
|
|
362
363
|
});
|
|
363
|
-
logger$
|
|
364
|
+
logger$14.log(`Generated cron handler: ${key}`);
|
|
364
365
|
}
|
|
365
366
|
return cronInfos;
|
|
366
367
|
}
|
|
@@ -396,7 +397,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
396
397
|
}
|
|
397
398
|
async build(context, constructs, outputDir, options) {
|
|
398
399
|
const provider = options?.provider || "aws-lambda";
|
|
399
|
-
const logger$
|
|
400
|
+
const logger$14 = console;
|
|
400
401
|
const functionInfos = [];
|
|
401
402
|
if (constructs.length === 0 || provider !== "aws-lambda") return functionInfos;
|
|
402
403
|
const functionsDir = (0, node_path.join)(outputDir, "functions");
|
|
@@ -410,7 +411,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
410
411
|
memorySize: construct.memorySize,
|
|
411
412
|
environment: await construct.getEnvironment()
|
|
412
413
|
});
|
|
413
|
-
logger$
|
|
414
|
+
logger$14.log(`Generated function handler: ${key}`);
|
|
414
415
|
}
|
|
415
416
|
return functionInfos;
|
|
416
417
|
}
|
|
@@ -443,11 +444,11 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
443
444
|
}
|
|
444
445
|
async build(context, constructs, outputDir, options) {
|
|
445
446
|
const provider = options?.provider || "aws-lambda";
|
|
446
|
-
const logger$
|
|
447
|
+
const logger$14 = console;
|
|
447
448
|
const subscriberInfos = [];
|
|
448
449
|
if (provider === "server") {
|
|
449
450
|
await this.generateServerSubscribersFile(outputDir, constructs);
|
|
450
|
-
logger$
|
|
451
|
+
logger$14.log(`Generated server subscribers file with ${constructs.length} subscribers (polling mode)`);
|
|
451
452
|
return subscriberInfos;
|
|
452
453
|
}
|
|
453
454
|
if (constructs.length === 0) return subscriberInfos;
|
|
@@ -464,7 +465,7 @@ var SubscriberGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
464
465
|
memorySize: construct.memorySize,
|
|
465
466
|
environment: await construct.getEnvironment()
|
|
466
467
|
});
|
|
467
|
-
logger$
|
|
468
|
+
logger$14.log(`Generated subscriber handler: ${key}`);
|
|
468
469
|
}
|
|
469
470
|
return subscriberInfos;
|
|
470
471
|
}
|
|
@@ -519,6 +520,7 @@ export const handler = adapter.handler;
|
|
|
519
520
|
* - sqs://region/account-id/queue-name (SQS queue)
|
|
520
521
|
* - sns://region/account-id/topic-name (SNS topic)
|
|
521
522
|
* - rabbitmq://host:port/queue-name (RabbitMQ)
|
|
523
|
+
* - pgboss://user:pass@host:port/database (pg-boss / PostgreSQL)
|
|
522
524
|
* - basic://in-memory (In-memory for testing)
|
|
523
525
|
*/
|
|
524
526
|
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
@@ -630,7 +632,7 @@ export async function setupSubscribers(
|
|
|
630
632
|
|
|
631
633
|
//#endregion
|
|
632
634
|
//#region src/workspace/client-generator.ts
|
|
633
|
-
const logger$
|
|
635
|
+
const logger$12 = console;
|
|
634
636
|
/**
|
|
635
637
|
* Get frontend apps that depend on a backend app.
|
|
636
638
|
*/
|
|
@@ -659,7 +661,7 @@ function countEndpoints(content) {
|
|
|
659
661
|
* Called when the backend's .gkm/openapi.ts file changes.
|
|
660
662
|
*/
|
|
661
663
|
async function copyClientToFrontends(workspace, backendAppName, options = {}) {
|
|
662
|
-
const log = options.silent ? () => {} : logger$
|
|
664
|
+
const log = options.silent ? () => {} : logger$12.log.bind(logger$12);
|
|
663
665
|
const results = [];
|
|
664
666
|
const backendApp = workspace.apps[backendAppName];
|
|
665
667
|
if (!backendApp || backendApp.type !== "backend") return results;
|
|
@@ -722,7 +724,7 @@ async function copyAllClients(workspace, options = {}) {
|
|
|
722
724
|
|
|
723
725
|
//#endregion
|
|
724
726
|
//#region src/dev/index.ts
|
|
725
|
-
const logger$
|
|
727
|
+
const logger$11 = console;
|
|
726
728
|
/**
|
|
727
729
|
* Load environment files
|
|
728
730
|
* @internal Exported for testing
|
|
@@ -773,7 +775,7 @@ async function findAvailablePort(preferredPort, maxAttempts = 10) {
|
|
|
773
775
|
for (let i = 0; i < maxAttempts; i++) {
|
|
774
776
|
const port = preferredPort + i;
|
|
775
777
|
if (await isPortAvailable(port)) return port;
|
|
776
|
-
logger$
|
|
778
|
+
logger$11.log(`⚠️ Port ${port} is in use, trying ${port + 1}...`);
|
|
777
779
|
}
|
|
778
780
|
throw new Error(`Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`);
|
|
779
781
|
}
|
|
@@ -856,27 +858,27 @@ async function resolveServicePorts(workspaceRoot) {
|
|
|
856
858
|
const savedState = await loadPortState(workspaceRoot);
|
|
857
859
|
const dockerEnv = {};
|
|
858
860
|
const ports = {};
|
|
859
|
-
logger$
|
|
861
|
+
logger$11.log("\n🔌 Resolving service ports...");
|
|
860
862
|
for (const mapping of mappings) {
|
|
861
863
|
const containerPort = getContainerHostPort(workspaceRoot, mapping.service, mapping.containerPort);
|
|
862
864
|
if (containerPort !== null) {
|
|
863
865
|
ports[mapping.envVar] = containerPort;
|
|
864
866
|
dockerEnv[mapping.envVar] = String(containerPort);
|
|
865
|
-
logger$
|
|
867
|
+
logger$11.log(` 🔄 ${mapping.service}:${mapping.containerPort}: reusing existing container on port ${containerPort}`);
|
|
866
868
|
continue;
|
|
867
869
|
}
|
|
868
870
|
const savedPort = savedState[mapping.envVar];
|
|
869
871
|
if (savedPort && await isPortAvailable(savedPort)) {
|
|
870
872
|
ports[mapping.envVar] = savedPort;
|
|
871
873
|
dockerEnv[mapping.envVar] = String(savedPort);
|
|
872
|
-
logger$
|
|
874
|
+
logger$11.log(` 💾 ${mapping.service}:${mapping.containerPort}: using saved port ${savedPort}`);
|
|
873
875
|
continue;
|
|
874
876
|
}
|
|
875
877
|
const resolvedPort = await findAvailablePort(mapping.defaultPort);
|
|
876
878
|
ports[mapping.envVar] = resolvedPort;
|
|
877
879
|
dockerEnv[mapping.envVar] = String(resolvedPort);
|
|
878
|
-
if (resolvedPort !== mapping.defaultPort) logger$
|
|
879
|
-
else logger$
|
|
880
|
+
if (resolvedPort !== mapping.defaultPort) logger$11.log(` ⚡ ${mapping.service}:${mapping.containerPort}: port ${mapping.defaultPort} occupied, using port ${resolvedPort}`);
|
|
881
|
+
else logger$11.log(` ✅ ${mapping.service}:${mapping.containerPort}: using default port ${resolvedPort}`);
|
|
880
882
|
}
|
|
881
883
|
await savePortState(workspaceRoot, ports);
|
|
882
884
|
return {
|
|
@@ -1020,7 +1022,7 @@ function getProductionConfigFromGkm(config) {
|
|
|
1020
1022
|
async function devCommand(options) {
|
|
1021
1023
|
if (options.entry) return entryDevCommand(options);
|
|
1022
1024
|
const defaultEnv = loadEnvFiles(".env");
|
|
1023
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1025
|
+
if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1024
1026
|
const appName = require_config.getAppNameFromCwd();
|
|
1025
1027
|
let config;
|
|
1026
1028
|
let appRoot = process.cwd();
|
|
@@ -1034,9 +1036,9 @@ async function devCommand(options) {
|
|
|
1034
1036
|
secretsRoot = appConfig.workspaceRoot;
|
|
1035
1037
|
workspaceAppName = appConfig.appName;
|
|
1036
1038
|
workspaceAppPort = appConfig.app.port;
|
|
1037
|
-
logger$
|
|
1039
|
+
logger$11.log(`📦 Running app: ${appConfig.appName} on port ${workspaceAppPort}`);
|
|
1038
1040
|
if (appConfig.app.entry) {
|
|
1039
|
-
logger$
|
|
1041
|
+
logger$11.log(`📄 Using entry point: ${appConfig.app.entry}`);
|
|
1040
1042
|
return entryDevCommand({
|
|
1041
1043
|
...options,
|
|
1042
1044
|
entry: appConfig.app.entry,
|
|
@@ -1047,7 +1049,7 @@ async function devCommand(options) {
|
|
|
1047
1049
|
} catch {
|
|
1048
1050
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
1049
1051
|
if (loadedConfig.type === "workspace") {
|
|
1050
|
-
logger$
|
|
1052
|
+
logger$11.log("📦 Detected workspace configuration");
|
|
1051
1053
|
return workspaceDevCommand(loadedConfig.workspace, options);
|
|
1052
1054
|
}
|
|
1053
1055
|
config = loadedConfig.raw;
|
|
@@ -1055,34 +1057,34 @@ async function devCommand(options) {
|
|
|
1055
1057
|
else {
|
|
1056
1058
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
1057
1059
|
if (loadedConfig.type === "workspace") {
|
|
1058
|
-
logger$
|
|
1060
|
+
logger$11.log("📦 Detected workspace configuration");
|
|
1059
1061
|
return workspaceDevCommand(loadedConfig.workspace, options);
|
|
1060
1062
|
}
|
|
1061
1063
|
config = loadedConfig.raw;
|
|
1062
1064
|
}
|
|
1063
1065
|
if (config.env) {
|
|
1064
1066
|
const { loaded, missing } = loadEnvFiles(config.env, appRoot);
|
|
1065
|
-
if (loaded.length > 0) logger$
|
|
1066
|
-
if (missing.length > 0) logger$
|
|
1067
|
+
if (loaded.length > 0) logger$11.log(`📦 Loaded env: ${loaded.join(", ")}`);
|
|
1068
|
+
if (missing.length > 0) logger$11.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
|
|
1067
1069
|
}
|
|
1068
1070
|
const resolved = resolveProviders(config, { provider: "server" });
|
|
1069
|
-
logger$
|
|
1070
|
-
logger$
|
|
1071
|
-
if (config.functions) logger$
|
|
1072
|
-
if (config.crons) logger$
|
|
1073
|
-
if (config.subscribers) logger$
|
|
1074
|
-
logger$
|
|
1071
|
+
logger$11.log("🚀 Starting development server...");
|
|
1072
|
+
logger$11.log(`Loading routes from: ${config.routes}`);
|
|
1073
|
+
if (config.functions) logger$11.log(`Loading functions from: ${config.functions}`);
|
|
1074
|
+
if (config.crons) logger$11.log(`Loading crons from: ${config.crons}`);
|
|
1075
|
+
if (config.subscribers) logger$11.log(`Loading subscribers from: ${config.subscribers}`);
|
|
1076
|
+
logger$11.log(`Using envParser: ${config.envParser}`);
|
|
1075
1077
|
const { path: envParserPath, importPattern: envParserImportPattern } = require_config.parseModuleConfig(config.envParser, "envParser");
|
|
1076
1078
|
const { path: loggerPath, importPattern: loggerImportPattern } = require_config.parseModuleConfig(config.logger, "logger");
|
|
1077
1079
|
const telescope = normalizeTelescopeConfig(config.telescope);
|
|
1078
|
-
if (telescope) logger$
|
|
1080
|
+
if (telescope) logger$11.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
1079
1081
|
const studio = normalizeStudioConfig(config.studio);
|
|
1080
|
-
if (studio) logger$
|
|
1082
|
+
if (studio) logger$11.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
1081
1083
|
const hooks = normalizeHooksConfig(config.hooks, appRoot);
|
|
1082
|
-
if (hooks) logger$
|
|
1084
|
+
if (hooks) logger$11.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);
|
|
1083
1085
|
const openApiConfig = require_openapi.resolveOpenApiConfig(config);
|
|
1084
1086
|
const enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;
|
|
1085
|
-
if (enableOpenApi) logger$
|
|
1087
|
+
if (enableOpenApi) logger$11.log(`📄 OpenAPI output: ${require_openapi.OPENAPI_OUTPUT_PATH}`);
|
|
1086
1088
|
const buildContext = {
|
|
1087
1089
|
envParserPath,
|
|
1088
1090
|
envParserImportPattern,
|
|
@@ -1102,7 +1104,7 @@ async function devCommand(options) {
|
|
|
1102
1104
|
await (0, node_fs_promises.mkdir)(secretsDir, { recursive: true });
|
|
1103
1105
|
secretsJsonPath = (0, node_path.join)(secretsDir, "dev-secrets.json");
|
|
1104
1106
|
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
|
|
1105
|
-
logger$
|
|
1107
|
+
logger$11.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
|
|
1106
1108
|
}
|
|
1107
1109
|
const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath);
|
|
1108
1110
|
await devServer.start();
|
|
@@ -1120,7 +1122,7 @@ async function devCommand(options) {
|
|
|
1120
1122
|
...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []
|
|
1121
1123
|
].flat().filter((p) => typeof p === "string");
|
|
1122
1124
|
const normalizedPatterns = watchPatterns.map((p) => p.startsWith("./") ? p.slice(2) : p);
|
|
1123
|
-
logger$
|
|
1125
|
+
logger$11.log(`👀 Watching for changes in: ${normalizedPatterns.join(", ")}`);
|
|
1124
1126
|
const resolvedFiles = await (0, fast_glob.default)(normalizedPatterns, {
|
|
1125
1127
|
cwd: appRoot,
|
|
1126
1128
|
absolute: false,
|
|
@@ -1130,7 +1132,7 @@ async function devCommand(options) {
|
|
|
1130
1132
|
const parts = f.split("/");
|
|
1131
1133
|
return parts.slice(0, -1).join("/");
|
|
1132
1134
|
}))];
|
|
1133
|
-
logger$
|
|
1135
|
+
logger$11.log(`📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`);
|
|
1134
1136
|
const watcher = chokidar.default.watch([...resolvedFiles, ...dirsToWatch], {
|
|
1135
1137
|
ignored: /(^|[/\\])\../,
|
|
1136
1138
|
persistent: true,
|
|
@@ -1138,27 +1140,27 @@ async function devCommand(options) {
|
|
|
1138
1140
|
cwd: appRoot
|
|
1139
1141
|
});
|
|
1140
1142
|
watcher.on("ready", () => {
|
|
1141
|
-
logger$
|
|
1143
|
+
logger$11.log("🔍 File watcher ready");
|
|
1142
1144
|
});
|
|
1143
1145
|
watcher.on("error", (error) => {
|
|
1144
|
-
logger$
|
|
1146
|
+
logger$11.error("❌ Watcher error:", error);
|
|
1145
1147
|
});
|
|
1146
1148
|
let rebuildTimeout = null;
|
|
1147
1149
|
watcher.on("change", async (path) => {
|
|
1148
|
-
logger$
|
|
1150
|
+
logger$11.log(`📝 File changed: ${path}`);
|
|
1149
1151
|
if (rebuildTimeout) clearTimeout(rebuildTimeout);
|
|
1150
1152
|
rebuildTimeout = setTimeout(async () => {
|
|
1151
1153
|
try {
|
|
1152
|
-
logger$
|
|
1154
|
+
logger$11.log("🔄 Rebuilding...");
|
|
1153
1155
|
await buildServer(config, buildContext, resolved.providers[0], enableOpenApi, appRoot, true);
|
|
1154
1156
|
if (enableOpenApi) await require_openapi.generateOpenApi(config, {
|
|
1155
1157
|
silent: true,
|
|
1156
1158
|
bustCache: true
|
|
1157
1159
|
});
|
|
1158
|
-
logger$
|
|
1160
|
+
logger$11.log("✅ Rebuild complete, restarting server...");
|
|
1159
1161
|
await devServer.restart();
|
|
1160
1162
|
} catch (error) {
|
|
1161
|
-
logger$
|
|
1163
|
+
logger$11.error("❌ Rebuild failed:", error.message);
|
|
1162
1164
|
}
|
|
1163
1165
|
}, 300);
|
|
1164
1166
|
});
|
|
@@ -1166,9 +1168,9 @@ async function devCommand(options) {
|
|
|
1166
1168
|
const shutdown = () => {
|
|
1167
1169
|
if (isShuttingDown) return;
|
|
1168
1170
|
isShuttingDown = true;
|
|
1169
|
-
logger$
|
|
1171
|
+
logger$11.log("\n🛑 Shutting down...");
|
|
1170
1172
|
Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
|
|
1171
|
-
logger$
|
|
1173
|
+
logger$11.error("Error during shutdown:", err);
|
|
1172
1174
|
}).finally(() => {
|
|
1173
1175
|
process.exit(0);
|
|
1174
1176
|
});
|
|
@@ -1271,11 +1273,11 @@ async function loadDevSecrets(workspace) {
|
|
|
1271
1273
|
for (const stage of stages) if (require_storage.secretsExist(stage, workspace.root)) {
|
|
1272
1274
|
const secrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
1273
1275
|
if (secrets) {
|
|
1274
|
-
logger$
|
|
1276
|
+
logger$11.log(`🔐 Loading secrets from stage: ${stage}`);
|
|
1275
1277
|
return require_storage.toEmbeddableSecrets(secrets);
|
|
1276
1278
|
}
|
|
1277
1279
|
}
|
|
1278
|
-
logger$
|
|
1280
|
+
logger$11.warn("⚠️ Secrets enabled but no dev/development secrets found. Run \"gkm setup\" to initialize your development environment");
|
|
1279
1281
|
return {};
|
|
1280
1282
|
}
|
|
1281
1283
|
/**
|
|
@@ -1290,7 +1292,7 @@ async function loadSecretsForApp(secretsRoot, appName) {
|
|
|
1290
1292
|
for (const stage of stages) if (require_storage.secretsExist(stage, secretsRoot)) {
|
|
1291
1293
|
const stageSecrets = await require_storage.readStageSecrets(stage, secretsRoot);
|
|
1292
1294
|
if (stageSecrets) {
|
|
1293
|
-
logger$
|
|
1295
|
+
logger$11.log(`🔐 Loading secrets from stage: ${stage}`);
|
|
1294
1296
|
secrets = require_storage.toEmbeddableSecrets(stageSecrets);
|
|
1295
1297
|
break;
|
|
1296
1298
|
}
|
|
@@ -1315,11 +1317,11 @@ async function startWorkspaceServices(workspace, portEnv) {
|
|
|
1315
1317
|
if (services.cache) servicesToStart.push("redis");
|
|
1316
1318
|
if (services.mail) servicesToStart.push("mailpit");
|
|
1317
1319
|
if (servicesToStart.length === 0) return;
|
|
1318
|
-
logger$
|
|
1320
|
+
logger$11.log(`🐳 Starting services: ${servicesToStart.join(", ")}`);
|
|
1319
1321
|
try {
|
|
1320
1322
|
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
1321
1323
|
if (!(0, node_fs.existsSync)(composeFile)) {
|
|
1322
|
-
logger$
|
|
1324
|
+
logger$11.warn("⚠️ No docker-compose.yml found. Services will not be started.");
|
|
1323
1325
|
return;
|
|
1324
1326
|
}
|
|
1325
1327
|
(0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
|
|
@@ -1330,9 +1332,9 @@ async function startWorkspaceServices(workspace, portEnv) {
|
|
|
1330
1332
|
...portEnv
|
|
1331
1333
|
}
|
|
1332
1334
|
});
|
|
1333
|
-
logger$
|
|
1335
|
+
logger$11.log("✅ Services started");
|
|
1334
1336
|
} catch (error) {
|
|
1335
|
-
logger$
|
|
1337
|
+
logger$11.error("❌ Failed to start services:", error.message);
|
|
1336
1338
|
throw error;
|
|
1337
1339
|
}
|
|
1338
1340
|
}
|
|
@@ -1349,41 +1351,41 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1349
1351
|
const appCount = Object.keys(workspace.apps).length;
|
|
1350
1352
|
const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend");
|
|
1351
1353
|
const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "frontend");
|
|
1352
|
-
logger$
|
|
1353
|
-
logger$
|
|
1354
|
+
logger$11.log(`\n🚀 Starting workspace: ${workspace.name}`);
|
|
1355
|
+
logger$11.log(` ${backendApps.length} backend app(s), ${frontendApps.length} frontend app(s)`);
|
|
1354
1356
|
const conflicts = checkPortConflicts(workspace);
|
|
1355
1357
|
if (conflicts.length > 0) {
|
|
1356
|
-
for (const conflict of conflicts) logger$
|
|
1358
|
+
for (const conflict of conflicts) logger$11.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
|
|
1357
1359
|
throw new Error("Port conflicts detected. Please assign unique ports to each app.");
|
|
1358
1360
|
}
|
|
1359
1361
|
if (frontendApps.length > 0) {
|
|
1360
|
-
logger$
|
|
1362
|
+
logger$11.log("\n🔍 Validating frontend apps...");
|
|
1361
1363
|
const validationResults = await validateFrontendApps(workspace);
|
|
1362
1364
|
let hasErrors = false;
|
|
1363
1365
|
for (const result of validationResults) {
|
|
1364
1366
|
if (!result.valid) {
|
|
1365
1367
|
hasErrors = true;
|
|
1366
|
-
logger$
|
|
1367
|
-
for (const error of result.errors) logger$
|
|
1368
|
+
logger$11.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
|
|
1369
|
+
for (const error of result.errors) logger$11.error(` • ${error}`);
|
|
1368
1370
|
}
|
|
1369
|
-
for (const warning of result.warnings) logger$
|
|
1371
|
+
for (const warning of result.warnings) logger$11.warn(` ⚠️ ${result.appName}: ${warning}`);
|
|
1370
1372
|
}
|
|
1371
1373
|
if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
|
|
1372
|
-
logger$
|
|
1374
|
+
logger$11.log("✅ Frontend apps validated");
|
|
1373
1375
|
}
|
|
1374
1376
|
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
1375
1377
|
const clientResults = await copyAllClients(workspace);
|
|
1376
1378
|
const copiedCount = clientResults.filter((r) => r.success).length;
|
|
1377
|
-
if (copiedCount > 0) logger$
|
|
1379
|
+
if (copiedCount > 0) logger$11.log(`\n📦 Copied ${copiedCount} API client(s)`);
|
|
1378
1380
|
}
|
|
1379
1381
|
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
1380
1382
|
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv);
|
|
1381
1383
|
const secretsEnv = rewriteUrlsWithPorts(await loadDevSecrets(workspace), resolvedPorts);
|
|
1382
|
-
if (Object.keys(secretsEnv).length > 0) logger$
|
|
1384
|
+
if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
|
|
1383
1385
|
const dependencyEnv = generateAllDependencyEnvVars(workspace);
|
|
1384
1386
|
if (Object.keys(dependencyEnv).length > 0) {
|
|
1385
|
-
logger$
|
|
1386
|
-
for (const [key, value] of Object.entries(dependencyEnv)) logger$
|
|
1387
|
+
logger$11.log("📡 Dependency URLs:");
|
|
1388
|
+
for (const [key, value] of Object.entries(dependencyEnv)) logger$11.log(` ${key}=${value}`);
|
|
1387
1389
|
}
|
|
1388
1390
|
let turboFilter = [];
|
|
1389
1391
|
if (options.app) {
|
|
@@ -1392,18 +1394,18 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1392
1394
|
throw new Error(`App "${options.app}" not found. Available apps: ${appNames}`);
|
|
1393
1395
|
}
|
|
1394
1396
|
turboFilter = ["--filter", options.app];
|
|
1395
|
-
logger$
|
|
1397
|
+
logger$11.log(`\n🎯 Running single app: ${options.app}`);
|
|
1396
1398
|
} else if (options.filter) {
|
|
1397
1399
|
turboFilter = ["--filter", options.filter];
|
|
1398
|
-
logger$
|
|
1399
|
-
} else logger$
|
|
1400
|
+
logger$11.log(`\n🔍 Using filter: ${options.filter}`);
|
|
1401
|
+
} else logger$11.log(`\n🎯 Running all ${appCount} apps`);
|
|
1400
1402
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
1401
|
-
logger$
|
|
1403
|
+
logger$11.log("\n📋 Apps (in dependency order):");
|
|
1402
1404
|
for (const appName of buildOrder) {
|
|
1403
1405
|
const app = workspace.apps[appName];
|
|
1404
1406
|
if (!app) continue;
|
|
1405
1407
|
const deps = app.dependencies.length > 0 ? ` (depends on: ${app.dependencies.join(", ")})` : "";
|
|
1406
|
-
logger$
|
|
1408
|
+
logger$11.log(` ${app.type === "backend" ? "🔧" : "🌐"} ${appName} → http://localhost:${app.port}${deps}`);
|
|
1407
1409
|
}
|
|
1408
1410
|
const configFiles = [
|
|
1409
1411
|
"gkm.config.ts",
|
|
@@ -1425,7 +1427,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1425
1427
|
NODE_ENV: "development",
|
|
1426
1428
|
...configPath ? { GKM_CONFIG_PATH: configPath } : {}
|
|
1427
1429
|
};
|
|
1428
|
-
logger$
|
|
1430
|
+
logger$11.log("\n🏃 Starting turbo run dev...\n");
|
|
1429
1431
|
const turboProcess = (0, node_child_process.spawn)("pnpm", [
|
|
1430
1432
|
"turbo",
|
|
1431
1433
|
"run",
|
|
@@ -1447,7 +1449,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1447
1449
|
});
|
|
1448
1450
|
}
|
|
1449
1451
|
if (openApiPaths.length > 0) {
|
|
1450
|
-
logger$
|
|
1452
|
+
logger$11.log(`\n👀 Watching ${openApiPaths.length} backend OpenAPI spec(s) for changes`);
|
|
1451
1453
|
const pathToApp = new Map(openApiPaths.map((p) => [p.path, p.appName]));
|
|
1452
1454
|
openApiWatcher = chokidar.default.watch(openApiPaths.map((p) => p.path), {
|
|
1453
1455
|
persistent: true,
|
|
@@ -1460,13 +1462,13 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1460
1462
|
copyTimeout = setTimeout(async () => {
|
|
1461
1463
|
const backendAppName = pathToApp.get(changedPath);
|
|
1462
1464
|
if (!backendAppName) return;
|
|
1463
|
-
logger$
|
|
1465
|
+
logger$11.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
|
|
1464
1466
|
try {
|
|
1465
1467
|
const results = await copyClientToFrontends(workspace, backendAppName, { silent: true });
|
|
1466
|
-
for (const result of results) if (result.success) logger$
|
|
1467
|
-
else if (result.error) logger$
|
|
1468
|
+
for (const result of results) if (result.success) logger$11.log(` 📦 Copied client to ${result.frontendApp} (${result.endpointCount} endpoints)`);
|
|
1469
|
+
else if (result.error) logger$11.error(` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`);
|
|
1468
1470
|
} catch (error) {
|
|
1469
|
-
logger$
|
|
1471
|
+
logger$11.error(` ❌ Failed to copy clients: ${error.message}`);
|
|
1470
1472
|
}
|
|
1471
1473
|
}, 200);
|
|
1472
1474
|
};
|
|
@@ -1478,7 +1480,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1478
1480
|
const shutdown = () => {
|
|
1479
1481
|
if (isShuttingDown) return;
|
|
1480
1482
|
isShuttingDown = true;
|
|
1481
|
-
logger$
|
|
1483
|
+
logger$11.log("\n🛑 Shutting down workspace...");
|
|
1482
1484
|
if (openApiWatcher) openApiWatcher.close().catch(() => {});
|
|
1483
1485
|
if (turboProcess.pid) try {
|
|
1484
1486
|
process.kill(-turboProcess.pid, "SIGTERM");
|
|
@@ -1493,7 +1495,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1493
1495
|
process.on("SIGTERM", shutdown);
|
|
1494
1496
|
return new Promise((resolve$3, reject) => {
|
|
1495
1497
|
turboProcess.on("error", (error) => {
|
|
1496
|
-
logger$
|
|
1498
|
+
logger$11.error("❌ Turbo error:", error);
|
|
1497
1499
|
reject(error);
|
|
1498
1500
|
});
|
|
1499
1501
|
turboProcess.on("exit", (code) => {
|
|
@@ -1606,7 +1608,7 @@ async function prepareEntryCredentials(options) {
|
|
|
1606
1608
|
secretsRoot = appInfo.workspaceRoot;
|
|
1607
1609
|
appName = appInfo.appName;
|
|
1608
1610
|
} catch (error) {
|
|
1609
|
-
logger$
|
|
1611
|
+
logger$11.log(`⚠️ Could not load workspace config: ${error.message}`);
|
|
1610
1612
|
secretsRoot = findSecretsRoot(cwd);
|
|
1611
1613
|
appName = require_config.getAppNameFromCwd(cwd) ?? void 0;
|
|
1612
1614
|
}
|
|
@@ -1636,11 +1638,11 @@ async function entryDevCommand(options) {
|
|
|
1636
1638
|
const entryPath = (0, node_path.resolve)(process.cwd(), entry);
|
|
1637
1639
|
if (!(0, node_fs.existsSync)(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
|
|
1638
1640
|
const defaultEnv = loadEnvFiles(".env");
|
|
1639
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1641
|
+
if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1640
1642
|
const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
|
|
1641
|
-
if (appName) logger$
|
|
1642
|
-
logger$
|
|
1643
|
-
if (Object.keys(credentials).length > 1) logger$
|
|
1643
|
+
if (appName) logger$11.log(`📦 App: ${appName} (port ${resolvedPort})`);
|
|
1644
|
+
logger$11.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
|
|
1645
|
+
if (Object.keys(credentials).length > 1) logger$11.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
|
|
1644
1646
|
const wrapperDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
1645
1647
|
await (0, node_fs_promises.mkdir)(wrapperDir, { recursive: true });
|
|
1646
1648
|
const wrapperPath = (0, node_path.join)(wrapperDir, "entry-wrapper.ts");
|
|
@@ -1651,7 +1653,7 @@ async function entryDevCommand(options) {
|
|
|
1651
1653
|
const shutdown = () => {
|
|
1652
1654
|
if (isShuttingDown) return;
|
|
1653
1655
|
isShuttingDown = true;
|
|
1654
|
-
logger$
|
|
1656
|
+
logger$11.log("\n🛑 Shutting down...");
|
|
1655
1657
|
runner.stop();
|
|
1656
1658
|
process.exit(0);
|
|
1657
1659
|
};
|
|
@@ -1683,14 +1685,14 @@ var EntryRunner = class {
|
|
|
1683
1685
|
});
|
|
1684
1686
|
let restartTimeout = null;
|
|
1685
1687
|
this.watcher.on("change", (path) => {
|
|
1686
|
-
logger$
|
|
1688
|
+
logger$11.log(`📝 File changed: ${path}`);
|
|
1687
1689
|
if (restartTimeout) clearTimeout(restartTimeout);
|
|
1688
1690
|
restartTimeout = setTimeout(async () => {
|
|
1689
|
-
logger$
|
|
1691
|
+
logger$11.log("🔄 Restarting...");
|
|
1690
1692
|
await this.restart();
|
|
1691
1693
|
}, 300);
|
|
1692
1694
|
});
|
|
1693
|
-
logger$
|
|
1695
|
+
logger$11.log(`👀 Watching for changes in: ${watchDir}`);
|
|
1694
1696
|
}
|
|
1695
1697
|
}
|
|
1696
1698
|
async runProcess() {
|
|
@@ -1705,14 +1707,14 @@ var EntryRunner = class {
|
|
|
1705
1707
|
});
|
|
1706
1708
|
this.isRunning = true;
|
|
1707
1709
|
this.childProcess.on("error", (error) => {
|
|
1708
|
-
logger$
|
|
1710
|
+
logger$11.error("❌ Process error:", error);
|
|
1709
1711
|
});
|
|
1710
1712
|
this.childProcess.on("exit", (code) => {
|
|
1711
|
-
if (code !== null && code !== 0 && code !== 143) logger$
|
|
1713
|
+
if (code !== null && code !== 0 && code !== 143) logger$11.error(`❌ Process exited with code ${code}`);
|
|
1712
1714
|
this.isRunning = false;
|
|
1713
1715
|
});
|
|
1714
1716
|
await new Promise((resolve$3) => setTimeout(resolve$3, 500));
|
|
1715
|
-
if (this.isRunning) logger$
|
|
1717
|
+
if (this.isRunning) logger$11.log(`\n🎉 Running at http://localhost:${this.port}`);
|
|
1716
1718
|
}
|
|
1717
1719
|
async restart() {
|
|
1718
1720
|
this.stopProcess();
|
|
@@ -1762,11 +1764,11 @@ var DevServer = class {
|
|
|
1762
1764
|
this.actualPort = this.requestedPort;
|
|
1763
1765
|
} else {
|
|
1764
1766
|
this.actualPort = await findAvailablePort(this.requestedPort);
|
|
1765
|
-
if (this.actualPort !== this.requestedPort) logger$
|
|
1767
|
+
if (this.actualPort !== this.requestedPort) logger$11.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
|
|
1766
1768
|
}
|
|
1767
1769
|
const serverEntryPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1768
1770
|
await this.createServerEntry();
|
|
1769
|
-
logger$
|
|
1771
|
+
logger$11.log(`\n✨ Starting server on port ${this.actualPort}...`);
|
|
1770
1772
|
this.serverProcess = (0, node_child_process.spawn)("npx", [
|
|
1771
1773
|
"tsx",
|
|
1772
1774
|
serverEntryPath,
|
|
@@ -1782,18 +1784,18 @@ var DevServer = class {
|
|
|
1782
1784
|
});
|
|
1783
1785
|
this.isRunning = true;
|
|
1784
1786
|
this.serverProcess.on("error", (error) => {
|
|
1785
|
-
logger$
|
|
1787
|
+
logger$11.error("❌ Server error:", error);
|
|
1786
1788
|
});
|
|
1787
1789
|
this.serverProcess.on("exit", (code, signal) => {
|
|
1788
|
-
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$
|
|
1790
|
+
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$11.error(`❌ Server exited with code ${code}`);
|
|
1789
1791
|
this.isRunning = false;
|
|
1790
1792
|
});
|
|
1791
1793
|
await new Promise((resolve$3) => setTimeout(resolve$3, 1e3));
|
|
1792
1794
|
if (this.isRunning) {
|
|
1793
|
-
logger$
|
|
1794
|
-
if (this.enableOpenApi) logger$
|
|
1795
|
-
if (this.telescope) logger$
|
|
1796
|
-
if (this.studio) logger$
|
|
1795
|
+
logger$11.log(`\n🎉 Server running at http://localhost:${this.actualPort}`);
|
|
1796
|
+
if (this.enableOpenApi) logger$11.log(`📚 API Docs available at http://localhost:${this.actualPort}/__docs`);
|
|
1797
|
+
if (this.telescope) logger$11.log(`🔭 Telescope available at http://localhost:${this.actualPort}${this.telescope.path}`);
|
|
1798
|
+
if (this.studio) logger$11.log(`🗄️ Studio available at http://localhost:${this.actualPort}${this.studio.path}`);
|
|
1797
1799
|
}
|
|
1798
1800
|
}
|
|
1799
1801
|
async stop() {
|
|
@@ -1831,9 +1833,9 @@ var DevServer = class {
|
|
|
1831
1833
|
}
|
|
1832
1834
|
async createServerEntry() {
|
|
1833
1835
|
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1834
|
-
const { relative: relative$6, dirname: dirname$
|
|
1836
|
+
const { relative: relative$6, dirname: dirname$11 } = await import("node:path");
|
|
1835
1837
|
const serverPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1836
|
-
const relativeAppPath = relative$6(dirname$
|
|
1838
|
+
const relativeAppPath = relative$6(dirname$11(serverPath), (0, node_path.join)(dirname$11(serverPath), "app.js"));
|
|
1837
1839
|
const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1838
1840
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1839
1841
|
|
|
@@ -1901,11 +1903,11 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1901
1903
|
const cwd = options.cwd ?? process.cwd();
|
|
1902
1904
|
if (commandArgs.length === 0) throw new Error("No command specified. Usage: gkm exec -- <command>");
|
|
1903
1905
|
const defaultEnv = loadEnvFiles(".env");
|
|
1904
|
-
if (defaultEnv.loaded.length > 0) logger$
|
|
1906
|
+
if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1905
1907
|
const { credentials, secretsJsonPath, appName, secretsRoot } = await prepareEntryCredentials({ cwd });
|
|
1906
|
-
if (appName) logger$
|
|
1908
|
+
if (appName) logger$11.log(`📦 App: ${appName}`);
|
|
1907
1909
|
const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
|
|
1908
|
-
if (secretCount > 0) logger$
|
|
1910
|
+
if (secretCount > 0) logger$11.log(`🔐 Loaded ${secretCount} secret(s)`);
|
|
1909
1911
|
const composePath = (0, node_path.join)(secretsRoot, "docker-compose.yml");
|
|
1910
1912
|
const mappings = parseComposePortMappings(composePath);
|
|
1911
1913
|
if (mappings.length > 0) {
|
|
@@ -1917,7 +1919,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1917
1919
|
mappings
|
|
1918
1920
|
});
|
|
1919
1921
|
Object.assign(credentials, rewritten);
|
|
1920
|
-
logger$
|
|
1922
|
+
logger$11.log(`🔌 Applied ${Object.keys(ports).length} port mapping(s)`);
|
|
1921
1923
|
}
|
|
1922
1924
|
}
|
|
1923
1925
|
try {
|
|
@@ -1934,7 +1936,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1934
1936
|
const [cmd, ...rawArgs] = commandArgs;
|
|
1935
1937
|
if (!cmd) throw new Error("No command specified");
|
|
1936
1938
|
const args = rawArgs.map((arg) => arg.replace(/\$PORT\b/g, credentials.PORT ?? "3000"));
|
|
1937
|
-
logger$
|
|
1939
|
+
logger$11.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
|
|
1938
1940
|
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
1939
1941
|
const tsxImport = "--import=tsx";
|
|
1940
1942
|
const preloadImport = `--import=${preloadPath}`;
|
|
@@ -1955,7 +1957,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1955
1957
|
const exitCode = await new Promise((resolve$3) => {
|
|
1956
1958
|
child.on("close", (code) => resolve$3(code ?? 0));
|
|
1957
1959
|
child.on("error", (error) => {
|
|
1958
|
-
logger$
|
|
1960
|
+
logger$11.error(`Failed to run command: ${error.message}`);
|
|
1959
1961
|
resolve$3(1);
|
|
1960
1962
|
});
|
|
1961
1963
|
});
|
|
@@ -1964,7 +1966,7 @@ async function execCommand(commandArgs, options = {}) {
|
|
|
1964
1966
|
|
|
1965
1967
|
//#endregion
|
|
1966
1968
|
//#region src/build/manifests.ts
|
|
1967
|
-
const logger$
|
|
1969
|
+
const logger$10 = console;
|
|
1968
1970
|
async function generateAwsManifest(outputDir, routes, functions, crons, subscribers) {
|
|
1969
1971
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
1970
1972
|
await (0, node_fs_promises.mkdir)(manifestDir, { recursive: true });
|
|
@@ -1989,8 +1991,8 @@ export type RoutePath = Route['path'];
|
|
|
1989
1991
|
`;
|
|
1990
1992
|
const manifestPath = (0, node_path.join)(manifestDir, "aws.ts");
|
|
1991
1993
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
1992
|
-
logger$
|
|
1993
|
-
logger$
|
|
1994
|
+
logger$10.log(`Generated AWS manifest with ${awsRoutes.length} routes, ${functions.length} functions, ${crons.length} crons, ${subscribers.length} subscribers`);
|
|
1995
|
+
logger$10.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
1994
1996
|
}
|
|
1995
1997
|
async function generateServerManifest(outputDir, appInfo, routes, subscribers) {
|
|
1996
1998
|
const manifestDir = (0, node_path.join)(outputDir, "manifest");
|
|
@@ -2021,13 +2023,13 @@ export type RoutePath = Route['path'];
|
|
|
2021
2023
|
`;
|
|
2022
2024
|
const manifestPath = (0, node_path.join)(manifestDir, "server.ts");
|
|
2023
2025
|
await (0, node_fs_promises.writeFile)(manifestPath, content);
|
|
2024
|
-
logger$
|
|
2025
|
-
logger$
|
|
2026
|
+
logger$10.log(`Generated server manifest with ${serverRoutes.length} routes, ${serverSubscribers.length} subscribers`);
|
|
2027
|
+
logger$10.log(`Manifest: ${(0, node_path.relative)(process.cwd(), manifestPath)}`);
|
|
2026
2028
|
}
|
|
2027
2029
|
|
|
2028
2030
|
//#endregion
|
|
2029
2031
|
//#region src/build/index.ts
|
|
2030
|
-
const logger$
|
|
2032
|
+
const logger$9 = console;
|
|
2031
2033
|
async function buildCommand(options) {
|
|
2032
2034
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
2033
2035
|
if (loadedConfig.type === "workspace") {
|
|
@@ -2035,7 +2037,7 @@ async function buildCommand(options) {
|
|
|
2035
2037
|
const workspaceRoot = (0, node_path.resolve)(loadedConfig.workspace.root);
|
|
2036
2038
|
const isAtWorkspaceRoot = cwd === workspaceRoot;
|
|
2037
2039
|
if (isAtWorkspaceRoot) {
|
|
2038
|
-
logger$
|
|
2040
|
+
logger$9.log("📦 Detected workspace configuration");
|
|
2039
2041
|
return workspaceBuildCommand(loadedConfig.workspace, options);
|
|
2040
2042
|
}
|
|
2041
2043
|
}
|
|
@@ -2043,21 +2045,21 @@ async function buildCommand(options) {
|
|
|
2043
2045
|
const resolved = resolveProviders(config, options);
|
|
2044
2046
|
const productionConfigFromGkm = getProductionConfigFromGkm(config);
|
|
2045
2047
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2046
|
-
if (production) logger$
|
|
2047
|
-
logger$
|
|
2048
|
-
logger$
|
|
2049
|
-
if (config.functions) logger$
|
|
2050
|
-
if (config.crons) logger$
|
|
2051
|
-
if (config.subscribers) logger$
|
|
2052
|
-
logger$
|
|
2048
|
+
if (production) logger$9.log(`🏭 Building for PRODUCTION`);
|
|
2049
|
+
logger$9.log(`Building with providers: ${resolved.providers.join(", ")}`);
|
|
2050
|
+
logger$9.log(`Loading routes from: ${config.routes}`);
|
|
2051
|
+
if (config.functions) logger$9.log(`Loading functions from: ${config.functions}`);
|
|
2052
|
+
if (config.crons) logger$9.log(`Loading crons from: ${config.crons}`);
|
|
2053
|
+
if (config.subscribers) logger$9.log(`Loading subscribers from: ${config.subscribers}`);
|
|
2054
|
+
logger$9.log(`Using envParser: ${config.envParser}`);
|
|
2053
2055
|
const { path: envParserPath, importPattern: envParserImportPattern } = require_config.parseModuleConfig(config.envParser, "envParser");
|
|
2054
2056
|
const { path: loggerPath, importPattern: loggerImportPattern } = require_config.parseModuleConfig(config.logger, "logger");
|
|
2055
2057
|
const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
|
|
2056
|
-
if (telescope) logger$
|
|
2058
|
+
if (telescope) logger$9.log(`🔭 Telescope enabled at ${telescope.path}`);
|
|
2057
2059
|
const studio = production ? void 0 : normalizeStudioConfig(config.studio);
|
|
2058
|
-
if (studio) logger$
|
|
2060
|
+
if (studio) logger$9.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
2059
2061
|
const hooks = normalizeHooksConfig(config.hooks);
|
|
2060
|
-
if (hooks) logger$
|
|
2062
|
+
if (hooks) logger$9.log(`🪝 Server hooks enabled`);
|
|
2061
2063
|
const services = config.docker?.compose?.services;
|
|
2062
2064
|
const dockerServices = services ? Array.isArray(services) ? {
|
|
2063
2065
|
postgres: services.includes("postgres"),
|
|
@@ -2089,12 +2091,12 @@ async function buildCommand(options) {
|
|
|
2089
2091
|
config.crons ? cronGenerator.load(config.crons) : [],
|
|
2090
2092
|
config.subscribers ? subscriberGenerator.load(config.subscribers) : []
|
|
2091
2093
|
]);
|
|
2092
|
-
logger$
|
|
2093
|
-
logger$
|
|
2094
|
-
logger$
|
|
2095
|
-
logger$
|
|
2094
|
+
logger$9.log(`Found ${allEndpoints.length} endpoints`);
|
|
2095
|
+
logger$9.log(`Found ${allFunctions.length} functions`);
|
|
2096
|
+
logger$9.log(`Found ${allCrons.length} crons`);
|
|
2097
|
+
logger$9.log(`Found ${allSubscribers.length} subscribers`);
|
|
2096
2098
|
if (allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0) {
|
|
2097
|
-
logger$
|
|
2099
|
+
logger$9.log("No endpoints, functions, crons, or subscribers found to process");
|
|
2098
2100
|
return {};
|
|
2099
2101
|
}
|
|
2100
2102
|
const rootOutputDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
@@ -2109,7 +2111,7 @@ async function buildCommand(options) {
|
|
|
2109
2111
|
async function buildForProvider(provider, context, rootOutputDir, endpointGenerator, functionGenerator, cronGenerator, subscriberGenerator, endpoints, functions, crons, subscribers, enableOpenApi, skipBundle, stage) {
|
|
2110
2112
|
const outputDir = (0, node_path.join)(process.cwd(), ".gkm", provider);
|
|
2111
2113
|
await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
|
|
2112
|
-
logger$
|
|
2114
|
+
logger$9.log(`\nGenerating handlers for provider: ${provider}`);
|
|
2113
2115
|
const [routes, functionInfos, cronInfos, subscriberInfos] = await Promise.all([
|
|
2114
2116
|
endpointGenerator.build(context, endpoints, outputDir, {
|
|
2115
2117
|
provider,
|
|
@@ -2119,7 +2121,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2119
2121
|
cronGenerator.build(context, crons, outputDir, { provider }),
|
|
2120
2122
|
subscriberGenerator.build(context, subscribers, outputDir, { provider })
|
|
2121
2123
|
]);
|
|
2122
|
-
logger$
|
|
2124
|
+
logger$9.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers for ${provider}`);
|
|
2123
2125
|
if (provider === "server") {
|
|
2124
2126
|
const routeMetadata = await Promise.all(endpoints.map(async ({ construct }) => ({
|
|
2125
2127
|
path: construct._path,
|
|
@@ -2134,8 +2136,8 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2134
2136
|
await generateServerManifest(rootOutputDir, appInfo, routeMetadata, subscriberInfos);
|
|
2135
2137
|
let masterKey;
|
|
2136
2138
|
if (context.production?.bundle && !skipBundle) {
|
|
2137
|
-
logger$
|
|
2138
|
-
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-
|
|
2139
|
+
logger$9.log(`\n📦 Bundling production server...`);
|
|
2140
|
+
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-CuMIfXw5.cjs"));
|
|
2139
2141
|
const allConstructs = [
|
|
2140
2142
|
...endpoints.map((e) => e.construct),
|
|
2141
2143
|
...functions.map((f) => f.construct),
|
|
@@ -2154,10 +2156,10 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2154
2156
|
dockerServices
|
|
2155
2157
|
});
|
|
2156
2158
|
masterKey = bundleResult.masterKey;
|
|
2157
|
-
logger$
|
|
2159
|
+
logger$9.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
|
|
2158
2160
|
if (masterKey) {
|
|
2159
|
-
logger$
|
|
2160
|
-
logger$
|
|
2161
|
+
logger$9.log(`\n🔐 Secrets encrypted for deployment`);
|
|
2162
|
+
logger$9.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
|
|
2161
2163
|
}
|
|
2162
2164
|
}
|
|
2163
2165
|
return { masterKey };
|
|
@@ -2194,17 +2196,17 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2194
2196
|
const apps = Object.entries(workspace.apps);
|
|
2195
2197
|
const backendApps = apps.filter(([, app]) => app.type === "backend");
|
|
2196
2198
|
const frontendApps = apps.filter(([, app]) => app.type === "frontend");
|
|
2197
|
-
logger$
|
|
2198
|
-
logger$
|
|
2199
|
-
logger$
|
|
2200
|
-
if (options.production) logger$
|
|
2199
|
+
logger$9.log(`\n🏗️ Building workspace: ${workspace.name}`);
|
|
2200
|
+
logger$9.log(` Backend apps: ${backendApps.map(([name$1]) => name$1).join(", ") || "none"}`);
|
|
2201
|
+
logger$9.log(` Frontend apps: ${frontendApps.map(([name$1]) => name$1).join(", ") || "none"}`);
|
|
2202
|
+
if (options.production) logger$9.log(` 🏭 Production mode enabled`);
|
|
2201
2203
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
2202
|
-
logger$
|
|
2204
|
+
logger$9.log(` Build order: ${buildOrder.join(" → ")}`);
|
|
2203
2205
|
const pm = detectPackageManager$2();
|
|
2204
|
-
logger$
|
|
2206
|
+
logger$9.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
|
|
2205
2207
|
try {
|
|
2206
2208
|
const turboCommand = getTurboCommand(pm);
|
|
2207
|
-
logger$
|
|
2209
|
+
logger$9.log(`Running: ${turboCommand}`);
|
|
2208
2210
|
await new Promise((resolve$3, reject) => {
|
|
2209
2211
|
const child = (0, node_child_process.spawn)(turboCommand, {
|
|
2210
2212
|
shell: true,
|
|
@@ -2232,15 +2234,15 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2232
2234
|
outputPath
|
|
2233
2235
|
});
|
|
2234
2236
|
}
|
|
2235
|
-
logger$
|
|
2236
|
-
logger$
|
|
2237
|
+
logger$9.log(`\n✅ Workspace build complete!`);
|
|
2238
|
+
logger$9.log(`\n📋 Build Summary:`);
|
|
2237
2239
|
for (const result of results) {
|
|
2238
2240
|
const icon = result.type === "backend" ? "⚙️" : "🌐";
|
|
2239
|
-
logger$
|
|
2241
|
+
logger$9.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
|
|
2240
2242
|
}
|
|
2241
2243
|
} catch (error) {
|
|
2242
2244
|
const errorMessage = error instanceof Error ? error.message : "Build failed";
|
|
2243
|
-
logger$
|
|
2245
|
+
logger$9.log(`\n❌ Build failed: ${errorMessage}`);
|
|
2244
2246
|
for (const [appName, app] of apps) results.push({
|
|
2245
2247
|
appName,
|
|
2246
2248
|
type: app.type,
|
|
@@ -2414,11 +2416,11 @@ async function createDnsProvider(options) {
|
|
|
2414
2416
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
2415
2417
|
const provider = config.provider;
|
|
2416
2418
|
if (provider === "hostinger") {
|
|
2417
|
-
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-
|
|
2419
|
+
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-CEsQbmpY.cjs"));
|
|
2418
2420
|
return new HostingerProvider();
|
|
2419
2421
|
}
|
|
2420
2422
|
if (provider === "route53") {
|
|
2421
|
-
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-
|
|
2423
|
+
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-BqXeHzuc.cjs"));
|
|
2422
2424
|
const route53Config = config;
|
|
2423
2425
|
return new Route53Provider({
|
|
2424
2426
|
region: route53Config.region,
|
|
@@ -2432,7 +2434,7 @@ async function createDnsProvider(options) {
|
|
|
2432
2434
|
|
|
2433
2435
|
//#endregion
|
|
2434
2436
|
//#region src/deploy/dns/index.ts
|
|
2435
|
-
const logger$
|
|
2437
|
+
const logger$8 = console;
|
|
2436
2438
|
/**
|
|
2437
2439
|
* Check if DNS config is legacy format (single domain with `domain` property)
|
|
2438
2440
|
*/
|
|
@@ -2469,7 +2471,7 @@ function groupHostnamesByDomain(appHostnames, dnsConfig) {
|
|
|
2469
2471
|
for (const [appName, hostname] of appHostnames) {
|
|
2470
2472
|
const rootDomain = findRootDomain(hostname, dnsConfig);
|
|
2471
2473
|
if (!rootDomain) {
|
|
2472
|
-
logger$
|
|
2474
|
+
logger$8.log(` ⚠ No DNS config found for hostname: ${hostname}`);
|
|
2473
2475
|
continue;
|
|
2474
2476
|
}
|
|
2475
2477
|
if (!grouped.has(rootDomain)) grouped.set(rootDomain, /* @__PURE__ */ new Map());
|
|
@@ -2521,10 +2523,10 @@ function generateRequiredRecords(appHostnames, rootDomain, serverIp) {
|
|
|
2521
2523
|
* Print DNS records table
|
|
2522
2524
|
*/
|
|
2523
2525
|
function printDnsRecordsTable(records, rootDomain) {
|
|
2524
|
-
logger$
|
|
2525
|
-
logger$
|
|
2526
|
-
logger$
|
|
2527
|
-
logger$
|
|
2526
|
+
logger$8.log(`\n 📋 DNS Records for ${rootDomain}:`);
|
|
2527
|
+
logger$8.log(" ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐");
|
|
2528
|
+
logger$8.log(" │ Subdomain │ Type │ Value │ Status │");
|
|
2529
|
+
logger$8.log(" ├─────────────────────────────────────┼──────┼─────────────────┼────────┤");
|
|
2528
2530
|
for (const record of records) {
|
|
2529
2531
|
const subdomain = record.subdomain.padEnd(35);
|
|
2530
2532
|
const type$1 = record.type.padEnd(4);
|
|
@@ -2534,18 +2536,18 @@ function printDnsRecordsTable(records, rootDomain) {
|
|
|
2534
2536
|
else if (record.created) status = "✓ new";
|
|
2535
2537
|
else if (record.existed) status = "✓";
|
|
2536
2538
|
else status = "?";
|
|
2537
|
-
logger$
|
|
2539
|
+
logger$8.log(` │ ${subdomain} │ ${type$1} │ ${value} │ ${status.padEnd(6)} │`);
|
|
2538
2540
|
}
|
|
2539
|
-
logger$
|
|
2541
|
+
logger$8.log(" └─────────────────────────────────────┴──────┴─────────────────┴────────┘");
|
|
2540
2542
|
}
|
|
2541
2543
|
/**
|
|
2542
2544
|
* Print DNS records in a simple format for manual setup
|
|
2543
2545
|
*/
|
|
2544
2546
|
function printDnsRecordsSimple(records, rootDomain) {
|
|
2545
|
-
logger$
|
|
2546
|
-
logger$
|
|
2547
|
-
for (const record of records) logger$
|
|
2548
|
-
logger$
|
|
2547
|
+
logger$8.log("\n 📋 Required DNS Records:");
|
|
2548
|
+
logger$8.log(` Add these A records to your DNS provider (${rootDomain}):\n`);
|
|
2549
|
+
for (const record of records) logger$8.log(` ${record.subdomain} → ${record.value} (A record)`);
|
|
2550
|
+
logger$8.log("");
|
|
2549
2551
|
}
|
|
2550
2552
|
/**
|
|
2551
2553
|
* Create DNS records for a single domain using its configured provider
|
|
@@ -2557,7 +2559,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2557
2559
|
provider = await createDnsProvider({ config: providerConfig });
|
|
2558
2560
|
} catch (error) {
|
|
2559
2561
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2560
|
-
logger$
|
|
2562
|
+
logger$8.log(` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`);
|
|
2561
2563
|
return records.map((r) => ({
|
|
2562
2564
|
...r,
|
|
2563
2565
|
error: message
|
|
@@ -2611,7 +2613,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2611
2613
|
}
|
|
2612
2614
|
} catch (error) {
|
|
2613
2615
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2614
|
-
logger$
|
|
2616
|
+
logger$8.log(` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`);
|
|
2615
2617
|
return records.map((r) => ({
|
|
2616
2618
|
hostname: r.hostname,
|
|
2617
2619
|
subdomain: r.subdomain,
|
|
@@ -2638,20 +2640,20 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2638
2640
|
async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
2639
2641
|
if (!dnsConfig) return null;
|
|
2640
2642
|
const normalizedConfig = normalizeDnsConfig(dnsConfig);
|
|
2641
|
-
logger$
|
|
2643
|
+
logger$8.log("\n🌐 Setting up DNS records...");
|
|
2642
2644
|
let serverIp;
|
|
2643
2645
|
try {
|
|
2644
2646
|
const endpointUrl = new URL(dokployEndpoint);
|
|
2645
2647
|
serverIp = await resolveHostnameToIp(endpointUrl.hostname);
|
|
2646
|
-
logger$
|
|
2648
|
+
logger$8.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);
|
|
2647
2649
|
} catch (error) {
|
|
2648
2650
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2649
|
-
logger$
|
|
2651
|
+
logger$8.log(` ⚠ Failed to resolve server IP: ${message}`);
|
|
2650
2652
|
return null;
|
|
2651
2653
|
}
|
|
2652
2654
|
const groupedHostnames = groupHostnamesByDomain(appHostnames, normalizedConfig);
|
|
2653
2655
|
if (groupedHostnames.size === 0) {
|
|
2654
|
-
logger$
|
|
2656
|
+
logger$8.log(" No DNS records needed (no hostnames match configured domains)");
|
|
2655
2657
|
return {
|
|
2656
2658
|
records: [],
|
|
2657
2659
|
success: true,
|
|
@@ -2663,22 +2665,22 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
2663
2665
|
for (const [rootDomain, domainHostnames] of groupedHostnames) {
|
|
2664
2666
|
const providerConfig = normalizedConfig[rootDomain];
|
|
2665
2667
|
if (!providerConfig) {
|
|
2666
|
-
logger$
|
|
2668
|
+
logger$8.log(` ⚠ No provider config for ${rootDomain}`);
|
|
2667
2669
|
continue;
|
|
2668
2670
|
}
|
|
2669
2671
|
const providerName = typeof providerConfig.provider === "string" ? providerConfig.provider : "custom";
|
|
2670
2672
|
const requiredRecords = generateRequiredRecords(domainHostnames, rootDomain, serverIp);
|
|
2671
2673
|
if (requiredRecords.length === 0) continue;
|
|
2672
|
-
logger$
|
|
2674
|
+
logger$8.log(` Creating DNS records for ${rootDomain} (${providerName})...`);
|
|
2673
2675
|
const domainRecords = await createDnsRecordsForDomain(requiredRecords, rootDomain, providerConfig);
|
|
2674
2676
|
allRecords.push(...domainRecords);
|
|
2675
2677
|
const created = domainRecords.filter((r) => r.created).length;
|
|
2676
2678
|
const existed = domainRecords.filter((r) => r.existed).length;
|
|
2677
2679
|
const failed = domainRecords.filter((r) => r.error).length;
|
|
2678
|
-
if (created > 0) logger$
|
|
2679
|
-
if (existed > 0) logger$
|
|
2680
|
+
if (created > 0) logger$8.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);
|
|
2681
|
+
if (existed > 0) logger$8.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);
|
|
2680
2682
|
if (failed > 0) {
|
|
2681
|
-
logger$
|
|
2683
|
+
logger$8.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
|
|
2682
2684
|
hasFailures = true;
|
|
2683
2685
|
}
|
|
2684
2686
|
if (state) {
|
|
@@ -2715,10 +2717,10 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
2715
2717
|
*/
|
|
2716
2718
|
async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
2717
2719
|
const results = [];
|
|
2718
|
-
logger$
|
|
2720
|
+
logger$8.log("\n🔍 Verifying DNS records...");
|
|
2719
2721
|
for (const [appName, hostname] of appHostnames) {
|
|
2720
2722
|
if (isDnsVerified(state, hostname, serverIp)) {
|
|
2721
|
-
logger$
|
|
2723
|
+
logger$8.log(` ✓ ${hostname} (previously verified)`);
|
|
2722
2724
|
results.push({
|
|
2723
2725
|
hostname,
|
|
2724
2726
|
appName,
|
|
@@ -2732,7 +2734,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
2732
2734
|
const resolvedIp = await resolveHostnameToIp(hostname);
|
|
2733
2735
|
if (resolvedIp === serverIp) {
|
|
2734
2736
|
setDnsVerification(state, hostname, serverIp);
|
|
2735
|
-
logger$
|
|
2737
|
+
logger$8.log(` ✓ ${hostname} → ${resolvedIp}`);
|
|
2736
2738
|
results.push({
|
|
2737
2739
|
hostname,
|
|
2738
2740
|
appName,
|
|
@@ -2741,7 +2743,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
2741
2743
|
expectedIp: serverIp
|
|
2742
2744
|
});
|
|
2743
2745
|
} else {
|
|
2744
|
-
logger$
|
|
2746
|
+
logger$8.log(` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`);
|
|
2745
2747
|
results.push({
|
|
2746
2748
|
hostname,
|
|
2747
2749
|
appName,
|
|
@@ -2752,7 +2754,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
2752
2754
|
}
|
|
2753
2755
|
} catch (error) {
|
|
2754
2756
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2755
|
-
logger$
|
|
2757
|
+
logger$8.log(` ⚠ ${hostname} DNS not propagated (${message})`);
|
|
2756
2758
|
results.push({
|
|
2757
2759
|
hostname,
|
|
2758
2760
|
appName,
|
|
@@ -2766,9 +2768,9 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
2766
2768
|
const skipped = results.filter((r) => r.skipped).length;
|
|
2767
2769
|
const pending = results.filter((r) => !r.verified).length;
|
|
2768
2770
|
if (pending > 0) {
|
|
2769
|
-
logger$
|
|
2770
|
-
logger$
|
|
2771
|
-
} else if (skipped > 0) logger$
|
|
2771
|
+
logger$8.log(`\n ${verified} verified, ${pending} pending propagation`);
|
|
2772
|
+
logger$8.log(" DNS changes may take 5-30 minutes to propagate");
|
|
2773
|
+
} else if (skipped > 0) logger$8.log(` ${verified} verified (${skipped} from cache)`);
|
|
2772
2774
|
return results;
|
|
2773
2775
|
}
|
|
2774
2776
|
|
|
@@ -3844,7 +3846,7 @@ CMD ["node", "index.mjs"]
|
|
|
3844
3846
|
|
|
3845
3847
|
//#endregion
|
|
3846
3848
|
//#region src/docker/index.ts
|
|
3847
|
-
const logger$
|
|
3849
|
+
const logger$7 = console;
|
|
3848
3850
|
/**
|
|
3849
3851
|
* Docker command implementation
|
|
3850
3852
|
* Generates Dockerfile, docker-compose.yml, and related files
|
|
@@ -3855,7 +3857,7 @@ const logger$5 = console;
|
|
|
3855
3857
|
async function dockerCommand(options) {
|
|
3856
3858
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
3857
3859
|
if (loadedConfig.type === "workspace") {
|
|
3858
|
-
logger$
|
|
3860
|
+
logger$7.log("📦 Detected workspace configuration");
|
|
3859
3861
|
return workspaceDockerCommand(loadedConfig.workspace, options);
|
|
3860
3862
|
}
|
|
3861
3863
|
const config = await require_config.loadConfig();
|
|
@@ -3876,14 +3878,14 @@ async function dockerCommand(options) {
|
|
|
3876
3878
|
let useTurbo = options.turbo ?? false;
|
|
3877
3879
|
if (inMonorepo && !useSlim) if (hasTurbo) {
|
|
3878
3880
|
useTurbo = true;
|
|
3879
|
-
logger$
|
|
3881
|
+
logger$7.log(" Detected monorepo with turbo.json - using turbo prune");
|
|
3880
3882
|
} 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");
|
|
3881
3883
|
let turboPackage = options.turboPackage ?? dockerConfig.imageName;
|
|
3882
3884
|
if (useTurbo && !options.turboPackage) try {
|
|
3883
3885
|
const pkg$1 = require(`${process.cwd()}/package.json`);
|
|
3884
3886
|
if (pkg$1.name) {
|
|
3885
3887
|
turboPackage = pkg$1.name;
|
|
3886
|
-
logger$
|
|
3888
|
+
logger$7.log(` Turbo package: ${turboPackage}`);
|
|
3887
3889
|
}
|
|
3888
3890
|
} catch {}
|
|
3889
3891
|
const templateOptions = {
|
|
@@ -3900,7 +3902,7 @@ async function dockerCommand(options) {
|
|
|
3900
3902
|
const dockerMode = useSlim ? "slim" : useTurbo ? "turbo" : "multi-stage";
|
|
3901
3903
|
const dockerfilePath = (0, node_path.join)(dockerDir, "Dockerfile");
|
|
3902
3904
|
await (0, node_fs_promises.writeFile)(dockerfilePath, dockerfile);
|
|
3903
|
-
logger$
|
|
3905
|
+
logger$7.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
|
|
3904
3906
|
const composeOptions = {
|
|
3905
3907
|
imageName: dockerConfig.imageName,
|
|
3906
3908
|
registry: options.registry ?? dockerConfig.registry,
|
|
@@ -3912,15 +3914,15 @@ async function dockerCommand(options) {
|
|
|
3912
3914
|
const dockerCompose = hasServices ? generateDockerCompose(composeOptions) : generateMinimalDockerCompose(composeOptions);
|
|
3913
3915
|
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
3914
3916
|
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
3915
|
-
logger$
|
|
3917
|
+
logger$7.log("Generated: .gkm/docker/docker-compose.yml");
|
|
3916
3918
|
const dockerignore = generateDockerignore();
|
|
3917
3919
|
const dockerignorePath = (0, node_path.join)(process.cwd(), ".dockerignore");
|
|
3918
3920
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
3919
|
-
logger$
|
|
3921
|
+
logger$7.log("Generated: .dockerignore (project root)");
|
|
3920
3922
|
const entrypoint = generateDockerEntrypoint();
|
|
3921
3923
|
const entrypointPath = (0, node_path.join)(dockerDir, "docker-entrypoint.sh");
|
|
3922
3924
|
await (0, node_fs_promises.writeFile)(entrypointPath, entrypoint);
|
|
3923
|
-
logger$
|
|
3925
|
+
logger$7.log("Generated: .gkm/docker/docker-entrypoint.sh");
|
|
3924
3926
|
const result = {
|
|
3925
3927
|
dockerfile: dockerfilePath,
|
|
3926
3928
|
dockerCompose: composePath,
|
|
@@ -3939,13 +3941,13 @@ async function dockerCommand(options) {
|
|
|
3939
3941
|
function ensureLockfile(cwd) {
|
|
3940
3942
|
const lockfilePath = findLockfilePath(cwd);
|
|
3941
3943
|
if (!lockfilePath) {
|
|
3942
|
-
logger$
|
|
3944
|
+
logger$7.warn("\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.");
|
|
3943
3945
|
return null;
|
|
3944
3946
|
}
|
|
3945
3947
|
const lockfileName = (0, node_path.basename)(lockfilePath);
|
|
3946
3948
|
const localLockfile = (0, node_path.join)(cwd, lockfileName);
|
|
3947
3949
|
if (lockfilePath === localLockfile) return null;
|
|
3948
|
-
logger$
|
|
3950
|
+
logger$7.log(` Copying ${lockfileName} from monorepo root...`);
|
|
3949
3951
|
(0, node_fs.copyFileSync)(lockfilePath, localLockfile);
|
|
3950
3952
|
return () => {
|
|
3951
3953
|
try {
|
|
@@ -3961,7 +3963,7 @@ async function buildDockerImage(imageName, options) {
|
|
|
3961
3963
|
const tag = options.tag ?? "latest";
|
|
3962
3964
|
const registry = options.registry;
|
|
3963
3965
|
const fullImageName = registry ? `${registry}/${imageName}:${tag}` : `${imageName}:${tag}`;
|
|
3964
|
-
logger$
|
|
3966
|
+
logger$7.log(`\n🐳 Building Docker image: ${fullImageName}`);
|
|
3965
3967
|
const cwd = process.cwd();
|
|
3966
3968
|
const cleanup = ensureLockfile(cwd);
|
|
3967
3969
|
try {
|
|
@@ -3973,7 +3975,7 @@ async function buildDockerImage(imageName, options) {
|
|
|
3973
3975
|
DOCKER_BUILDKIT: "1"
|
|
3974
3976
|
}
|
|
3975
3977
|
});
|
|
3976
|
-
logger$
|
|
3978
|
+
logger$7.log(`✅ Docker image built: ${fullImageName}`);
|
|
3977
3979
|
} catch (error) {
|
|
3978
3980
|
throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3979
3981
|
} finally {
|
|
@@ -3988,13 +3990,13 @@ async function pushDockerImage(imageName, options) {
|
|
|
3988
3990
|
const registry = options.registry;
|
|
3989
3991
|
if (!registry) throw new Error("Registry is required to push Docker image. Use --registry or configure docker.registry in gkm.config.ts");
|
|
3990
3992
|
const fullImageName = `${registry}/${imageName}:${tag}`;
|
|
3991
|
-
logger$
|
|
3993
|
+
logger$7.log(`\n🚀 Pushing Docker image: ${fullImageName}`);
|
|
3992
3994
|
try {
|
|
3993
3995
|
(0, node_child_process.execSync)(`docker push ${fullImageName}`, {
|
|
3994
3996
|
cwd: process.cwd(),
|
|
3995
3997
|
stdio: "inherit"
|
|
3996
3998
|
});
|
|
3997
|
-
logger$
|
|
3999
|
+
logger$7.log(`✅ Docker image pushed: ${fullImageName}`);
|
|
3998
4000
|
} catch (error) {
|
|
3999
4001
|
throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4000
4002
|
}
|
|
@@ -4020,11 +4022,11 @@ function getAppPackageName(appPath) {
|
|
|
4020
4022
|
async function workspaceDockerCommand(workspace, options) {
|
|
4021
4023
|
const results = [];
|
|
4022
4024
|
const apps = Object.entries(workspace.apps);
|
|
4023
|
-
logger$
|
|
4025
|
+
logger$7.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
|
|
4024
4026
|
const dockerDir = (0, node_path.join)(workspace.root, ".gkm", "docker");
|
|
4025
4027
|
await (0, node_fs_promises.mkdir)(dockerDir, { recursive: true });
|
|
4026
4028
|
const packageManager = detectPackageManager$1(workspace.root);
|
|
4027
|
-
logger$
|
|
4029
|
+
logger$7.log(` Package manager: ${packageManager}`);
|
|
4028
4030
|
for (const [appName, app] of apps) {
|
|
4029
4031
|
const appPath = app.path;
|
|
4030
4032
|
const fullAppPath = (0, node_path.join)(workspace.root, appPath);
|
|
@@ -4032,7 +4034,7 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4032
4034
|
const imageName = appName;
|
|
4033
4035
|
const hasEntry = !!app.entry;
|
|
4034
4036
|
const buildType = hasEntry ? "entry" : app.type;
|
|
4035
|
-
logger$
|
|
4037
|
+
logger$7.log(`\n 📄 Generating Dockerfile for ${appName} (${buildType})`);
|
|
4036
4038
|
let dockerfile;
|
|
4037
4039
|
if (app.type === "frontend") dockerfile = generateNextjsDockerfile({
|
|
4038
4040
|
imageName,
|
|
@@ -4063,7 +4065,7 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4063
4065
|
});
|
|
4064
4066
|
const dockerfilePath = (0, node_path.join)(dockerDir, `Dockerfile.${appName}`);
|
|
4065
4067
|
await (0, node_fs_promises.writeFile)(dockerfilePath, dockerfile);
|
|
4066
|
-
logger$
|
|
4068
|
+
logger$7.log(` Generated: .gkm/docker/Dockerfile.${appName}`);
|
|
4067
4069
|
results.push({
|
|
4068
4070
|
appName,
|
|
4069
4071
|
type: app.type,
|
|
@@ -4074,19 +4076,19 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
4074
4076
|
const dockerignore = generateDockerignore();
|
|
4075
4077
|
const dockerignorePath = (0, node_path.join)(workspace.root, ".dockerignore");
|
|
4076
4078
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
4077
|
-
logger$
|
|
4079
|
+
logger$7.log(`\n Generated: .dockerignore (workspace root)`);
|
|
4078
4080
|
const dockerCompose = generateWorkspaceCompose(workspace, { registry: options.registry });
|
|
4079
4081
|
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
4080
4082
|
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
4081
|
-
logger$
|
|
4082
|
-
logger$
|
|
4083
|
-
logger$
|
|
4083
|
+
logger$7.log(` Generated: .gkm/docker/docker-compose.yml`);
|
|
4084
|
+
logger$7.log(`\n✅ Generated ${results.length} Dockerfile(s) + docker-compose.yml`);
|
|
4085
|
+
logger$7.log("\n📋 Build commands:");
|
|
4084
4086
|
for (const result of results) {
|
|
4085
4087
|
const icon = result.type === "backend" ? "⚙️" : "🌐";
|
|
4086
|
-
logger$
|
|
4088
|
+
logger$7.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
|
|
4087
4089
|
}
|
|
4088
|
-
logger$
|
|
4089
|
-
logger$
|
|
4090
|
+
logger$7.log("\n📋 Run all services:");
|
|
4091
|
+
logger$7.log(" docker compose -f .gkm/docker/docker-compose.yml up --build");
|
|
4090
4092
|
return {
|
|
4091
4093
|
apps: results,
|
|
4092
4094
|
dockerCompose: composePath,
|
|
@@ -4126,7 +4128,7 @@ function getAppNameFromPackageJson() {
|
|
|
4126
4128
|
} catch {}
|
|
4127
4129
|
return void 0;
|
|
4128
4130
|
}
|
|
4129
|
-
const logger$
|
|
4131
|
+
const logger$6 = console;
|
|
4130
4132
|
/**
|
|
4131
4133
|
* Get the full image reference
|
|
4132
4134
|
*/
|
|
@@ -4141,18 +4143,18 @@ function getImageRef(registry, imageName, tag) {
|
|
|
4141
4143
|
* @param buildArgs - Build arguments to pass to docker build
|
|
4142
4144
|
*/
|
|
4143
4145
|
async function buildImage(imageRef, appName, buildArgs) {
|
|
4144
|
-
logger$
|
|
4146
|
+
logger$6.log(`\n🔨 Building Docker image: ${imageRef}`);
|
|
4145
4147
|
const cwd = process.cwd();
|
|
4146
4148
|
const lockfilePath = findLockfilePath(cwd);
|
|
4147
4149
|
const lockfileDir = lockfilePath ? (0, node_path.dirname)(lockfilePath) : cwd;
|
|
4148
4150
|
const inMonorepo = lockfileDir !== cwd;
|
|
4149
|
-
if (appName || inMonorepo) logger$
|
|
4150
|
-
else logger$
|
|
4151
|
+
if (appName || inMonorepo) logger$6.log(" Generating Dockerfile for monorepo (turbo prune)...");
|
|
4152
|
+
else logger$6.log(" Generating Dockerfile...");
|
|
4151
4153
|
await dockerCommand({});
|
|
4152
4154
|
const dockerfileSuffix = appName ? `.${appName}` : "";
|
|
4153
4155
|
const dockerfilePath = `.gkm/docker/Dockerfile${dockerfileSuffix}`;
|
|
4154
4156
|
const buildCwd = lockfilePath && (inMonorepo || appName) ? lockfileDir : cwd;
|
|
4155
|
-
if (buildCwd !== cwd) logger$
|
|
4157
|
+
if (buildCwd !== cwd) logger$6.log(` Building from workspace root: ${buildCwd}`);
|
|
4156
4158
|
const buildArgsString = buildArgs && buildArgs.length > 0 ? buildArgs.map((arg) => `--build-arg "${arg}"`).join(" ") : "";
|
|
4157
4159
|
try {
|
|
4158
4160
|
const cmd = [
|
|
@@ -4171,7 +4173,7 @@ async function buildImage(imageRef, appName, buildArgs) {
|
|
|
4171
4173
|
DOCKER_BUILDKIT: "1"
|
|
4172
4174
|
}
|
|
4173
4175
|
});
|
|
4174
|
-
logger$
|
|
4176
|
+
logger$6.log(`✅ Image built: ${imageRef}`);
|
|
4175
4177
|
} catch (error) {
|
|
4176
4178
|
throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4177
4179
|
}
|
|
@@ -4180,13 +4182,13 @@ async function buildImage(imageRef, appName, buildArgs) {
|
|
|
4180
4182
|
* Push Docker image to registry
|
|
4181
4183
|
*/
|
|
4182
4184
|
async function pushImage(imageRef) {
|
|
4183
|
-
logger$
|
|
4185
|
+
logger$6.log(`\n☁️ Pushing image: ${imageRef}`);
|
|
4184
4186
|
try {
|
|
4185
4187
|
(0, node_child_process.execSync)(`docker push ${imageRef}`, {
|
|
4186
4188
|
cwd: process.cwd(),
|
|
4187
4189
|
stdio: "inherit"
|
|
4188
4190
|
});
|
|
4189
|
-
logger$
|
|
4191
|
+
logger$6.log(`✅ Image pushed: ${imageRef}`);
|
|
4190
4192
|
} catch (error) {
|
|
4191
4193
|
throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4192
4194
|
}
|
|
@@ -4199,17 +4201,17 @@ async function deployDocker(options) {
|
|
|
4199
4201
|
const imageName = config.imageName;
|
|
4200
4202
|
const imageRef = getImageRef(config.registry, imageName, tag);
|
|
4201
4203
|
await buildImage(imageRef, config.appName, buildArgs);
|
|
4202
|
-
if (!skipPush) if (!config.registry) logger$
|
|
4204
|
+
if (!skipPush) if (!config.registry) logger$6.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
4203
4205
|
else await pushImage(imageRef);
|
|
4204
|
-
logger$
|
|
4205
|
-
logger$
|
|
4206
|
-
logger$
|
|
4207
|
-
logger$
|
|
4206
|
+
logger$6.log("\n✅ Docker deployment ready!");
|
|
4207
|
+
logger$6.log(`\n📋 Deployment details:`);
|
|
4208
|
+
logger$6.log(` Image: ${imageRef}`);
|
|
4209
|
+
logger$6.log(` Stage: ${stage}`);
|
|
4208
4210
|
if (masterKey) {
|
|
4209
|
-
logger$
|
|
4210
|
-
logger$
|
|
4211
|
-
logger$
|
|
4212
|
-
logger$
|
|
4211
|
+
logger$6.log(`\n🔐 Deploy with this environment variable:`);
|
|
4212
|
+
logger$6.log(` GKM_MASTER_KEY=${masterKey}`);
|
|
4213
|
+
logger$6.log("\n Example docker run:");
|
|
4214
|
+
logger$6.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);
|
|
4213
4215
|
}
|
|
4214
4216
|
return {
|
|
4215
4217
|
imageRef,
|
|
@@ -4236,7 +4238,7 @@ function resolveDockerConfig(config) {
|
|
|
4236
4238
|
|
|
4237
4239
|
//#endregion
|
|
4238
4240
|
//#region src/deploy/dokploy.ts
|
|
4239
|
-
const logger$
|
|
4241
|
+
const logger$5 = console;
|
|
4240
4242
|
/**
|
|
4241
4243
|
* Get the Dokploy API token from stored credentials or environment
|
|
4242
4244
|
*/
|
|
@@ -4260,25 +4262,25 @@ async function createApi$1(endpoint) {
|
|
|
4260
4262
|
*/
|
|
4261
4263
|
async function deployDokploy(options) {
|
|
4262
4264
|
const { stage, imageRef, masterKey, config } = options;
|
|
4263
|
-
logger$
|
|
4264
|
-
logger$
|
|
4265
|
-
logger$
|
|
4265
|
+
logger$5.log(`\n🎯 Deploying to Dokploy...`);
|
|
4266
|
+
logger$5.log(` Endpoint: ${config.endpoint}`);
|
|
4267
|
+
logger$5.log(` Application: ${config.applicationId}`);
|
|
4266
4268
|
const api = await createApi$1(config.endpoint);
|
|
4267
|
-
logger$
|
|
4269
|
+
logger$5.log(` Configuring Docker image: ${imageRef}`);
|
|
4268
4270
|
const registryOptions = {};
|
|
4269
4271
|
if (config.registryId) {
|
|
4270
4272
|
registryOptions.registryId = config.registryId;
|
|
4271
|
-
logger$
|
|
4273
|
+
logger$5.log(` Using Dokploy registry: ${config.registryId}`);
|
|
4272
4274
|
} else {
|
|
4273
4275
|
const storedRegistryId = await require_credentials.getDokployRegistryId();
|
|
4274
4276
|
if (storedRegistryId) {
|
|
4275
4277
|
registryOptions.registryId = storedRegistryId;
|
|
4276
|
-
logger$
|
|
4278
|
+
logger$5.log(` Using stored Dokploy registry: ${storedRegistryId}`);
|
|
4277
4279
|
} else if (config.registryCredentials) {
|
|
4278
4280
|
registryOptions.username = config.registryCredentials.username;
|
|
4279
4281
|
registryOptions.password = config.registryCredentials.password;
|
|
4280
4282
|
registryOptions.registryUrl = config.registryCredentials.registryUrl;
|
|
4281
|
-
logger$
|
|
4283
|
+
logger$5.log(` Using registry credentials for: ${config.registryCredentials.registryUrl}`);
|
|
4282
4284
|
} else {
|
|
4283
4285
|
const username = process.env.DOCKER_REGISTRY_USERNAME;
|
|
4284
4286
|
const password = process.env.DOCKER_REGISTRY_PASSWORD;
|
|
@@ -4287,31 +4289,31 @@ async function deployDokploy(options) {
|
|
|
4287
4289
|
registryOptions.username = username;
|
|
4288
4290
|
registryOptions.password = password;
|
|
4289
4291
|
registryOptions.registryUrl = registryUrl;
|
|
4290
|
-
logger$
|
|
4292
|
+
logger$5.log(` Using registry credentials from environment`);
|
|
4291
4293
|
}
|
|
4292
4294
|
}
|
|
4293
4295
|
}
|
|
4294
4296
|
await api.saveDockerProvider(config.applicationId, imageRef, registryOptions);
|
|
4295
|
-
logger$
|
|
4297
|
+
logger$5.log(" ✓ Docker provider configured");
|
|
4296
4298
|
const envVars = {};
|
|
4297
4299
|
if (masterKey) envVars.GKM_MASTER_KEY = masterKey;
|
|
4298
4300
|
if (Object.keys(envVars).length > 0) {
|
|
4299
|
-
logger$
|
|
4301
|
+
logger$5.log(" Updating environment variables...");
|
|
4300
4302
|
const envString = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
4301
4303
|
await api.saveApplicationEnv(config.applicationId, envString);
|
|
4302
|
-
logger$
|
|
4304
|
+
logger$5.log(" ✓ Environment variables updated");
|
|
4303
4305
|
}
|
|
4304
|
-
logger$
|
|
4306
|
+
logger$5.log(" Triggering deployment...");
|
|
4305
4307
|
await api.deployApplication(config.applicationId);
|
|
4306
|
-
logger$
|
|
4307
|
-
logger$
|
|
4308
|
-
logger$
|
|
4309
|
-
logger$
|
|
4310
|
-
logger$
|
|
4311
|
-
logger$
|
|
4312
|
-
if (masterKey) logger$
|
|
4308
|
+
logger$5.log(" ✓ Deployment triggered");
|
|
4309
|
+
logger$5.log("\n✅ Dokploy deployment initiated!");
|
|
4310
|
+
logger$5.log(`\n📋 Deployment details:`);
|
|
4311
|
+
logger$5.log(` Image: ${imageRef}`);
|
|
4312
|
+
logger$5.log(` Stage: ${stage}`);
|
|
4313
|
+
logger$5.log(` Application ID: ${config.applicationId}`);
|
|
4314
|
+
if (masterKey) logger$5.log(`\n🔐 GKM_MASTER_KEY has been set in Dokploy environment`);
|
|
4313
4315
|
const deploymentUrl = `${config.endpoint}/project/${config.projectId}`;
|
|
4314
|
-
logger$
|
|
4316
|
+
logger$5.log(`\n🔗 View deployment: ${deploymentUrl}`);
|
|
4315
4317
|
return {
|
|
4316
4318
|
imageRef,
|
|
4317
4319
|
masterKey,
|
|
@@ -4477,7 +4479,7 @@ function validateEnvVars(requiredVars, context) {
|
|
|
4477
4479
|
|
|
4478
4480
|
//#endregion
|
|
4479
4481
|
//#region src/deploy/init.ts
|
|
4480
|
-
const logger$
|
|
4482
|
+
const logger$4 = console;
|
|
4481
4483
|
/**
|
|
4482
4484
|
* Get the Dokploy API token from stored credentials or environment
|
|
4483
4485
|
*/
|
|
@@ -4511,20 +4513,20 @@ async function createApi(endpoint) {
|
|
|
4511
4513
|
async function updateConfig(config, cwd = process.cwd()) {
|
|
4512
4514
|
const configPath = (0, node_path.join)(cwd, "gkm.config.ts");
|
|
4513
4515
|
if (!(0, node_fs.existsSync)(configPath)) {
|
|
4514
|
-
logger$
|
|
4515
|
-
logger$
|
|
4516
|
-
logger$
|
|
4517
|
-
logger$
|
|
4518
|
-
logger$
|
|
4519
|
-
logger$
|
|
4520
|
-
logger$
|
|
4521
|
-
logger$
|
|
4516
|
+
logger$4.warn("\n gkm.config.ts not found. Add this configuration manually:\n");
|
|
4517
|
+
logger$4.log(` providers: {`);
|
|
4518
|
+
logger$4.log(` dokploy: {`);
|
|
4519
|
+
logger$4.log(` endpoint: '${config.endpoint}',`);
|
|
4520
|
+
logger$4.log(` projectId: '${config.projectId}',`);
|
|
4521
|
+
logger$4.log(` applicationId: '${config.applicationId}',`);
|
|
4522
|
+
logger$4.log(` },`);
|
|
4523
|
+
logger$4.log(` },`);
|
|
4522
4524
|
return;
|
|
4523
4525
|
}
|
|
4524
4526
|
const content = await (0, node_fs_promises.readFile)(configPath, "utf-8");
|
|
4525
4527
|
if (content.includes("dokploy:") && content.includes("applicationId:")) {
|
|
4526
|
-
logger$
|
|
4527
|
-
logger$
|
|
4528
|
+
logger$4.log("\n Dokploy config already exists in gkm.config.ts");
|
|
4529
|
+
logger$4.log(" Updating with new values...");
|
|
4528
4530
|
}
|
|
4529
4531
|
const registryLine = config.registryId ? `\n\t\t\tregistryId: '${config.registryId}',` : "";
|
|
4530
4532
|
const dokployConfigStr = `dokploy: {
|
|
@@ -4541,7 +4543,7 @@ async function updateConfig(config, cwd = process.cwd()) {
|
|
|
4541
4543
|
},
|
|
4542
4544
|
});`);
|
|
4543
4545
|
await (0, node_fs_promises.writeFile)(configPath, newContent);
|
|
4544
|
-
logger$
|
|
4546
|
+
logger$4.log("\n ✓ Updated gkm.config.ts with Dokploy configuration");
|
|
4545
4547
|
}
|
|
4546
4548
|
/**
|
|
4547
4549
|
* Initialize Dokploy deployment configuration
|
|
@@ -4550,24 +4552,24 @@ async function deployInitCommand(options) {
|
|
|
4550
4552
|
const { projectName, appName, projectId: existingProjectId, registryId } = options;
|
|
4551
4553
|
const endpoint = await getEndpoint(options.endpoint);
|
|
4552
4554
|
const api = await createApi(endpoint);
|
|
4553
|
-
logger$
|
|
4554
|
-
logger$
|
|
4555
|
+
logger$4.log(`\n🚀 Initializing Dokploy deployment...`);
|
|
4556
|
+
logger$4.log(` Endpoint: ${endpoint}`);
|
|
4555
4557
|
let projectId;
|
|
4556
4558
|
if (existingProjectId) {
|
|
4557
4559
|
projectId = existingProjectId;
|
|
4558
|
-
logger$
|
|
4560
|
+
logger$4.log(`\n📁 Using existing project: ${projectId}`);
|
|
4559
4561
|
} else {
|
|
4560
|
-
logger$
|
|
4562
|
+
logger$4.log(`\n📁 Looking for project: ${projectName}`);
|
|
4561
4563
|
const projects = await api.listProjects();
|
|
4562
4564
|
const existingProject = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
4563
4565
|
if (existingProject) {
|
|
4564
4566
|
projectId = existingProject.projectId;
|
|
4565
|
-
logger$
|
|
4567
|
+
logger$4.log(` Found existing project: ${projectId}`);
|
|
4566
4568
|
} else {
|
|
4567
|
-
logger$
|
|
4569
|
+
logger$4.log(` Creating new project...`);
|
|
4568
4570
|
const result = await api.createProject(projectName);
|
|
4569
4571
|
projectId = result.project.projectId;
|
|
4570
|
-
logger$
|
|
4572
|
+
logger$4.log(` ✓ Created project: ${projectId}`);
|
|
4571
4573
|
}
|
|
4572
4574
|
}
|
|
4573
4575
|
const project = await api.getProject(projectId);
|
|
@@ -4575,23 +4577,23 @@ async function deployInitCommand(options) {
|
|
|
4575
4577
|
const firstEnv = project.environments?.[0];
|
|
4576
4578
|
if (firstEnv) environmentId = firstEnv.environmentId;
|
|
4577
4579
|
else {
|
|
4578
|
-
logger$
|
|
4580
|
+
logger$4.log(` Creating production environment...`);
|
|
4579
4581
|
const env = await api.createEnvironment(projectId, "production");
|
|
4580
4582
|
environmentId = env.environmentId;
|
|
4581
4583
|
}
|
|
4582
|
-
logger$
|
|
4584
|
+
logger$4.log(`\n📦 Creating application: ${appName}`);
|
|
4583
4585
|
const application = await api.createApplication(appName, projectId, environmentId);
|
|
4584
|
-
logger$
|
|
4586
|
+
logger$4.log(` ✓ Created application: ${application.applicationId}`);
|
|
4585
4587
|
if (registryId) {
|
|
4586
|
-
logger$
|
|
4588
|
+
logger$4.log(`\n🔧 Configuring registry: ${registryId}`);
|
|
4587
4589
|
await api.updateApplication(application.applicationId, { registryId });
|
|
4588
|
-
logger$
|
|
4590
|
+
logger$4.log(` ✓ Registry configured`);
|
|
4589
4591
|
} else try {
|
|
4590
4592
|
const registries = await api.listRegistries();
|
|
4591
4593
|
if (registries.length > 0) {
|
|
4592
|
-
logger$
|
|
4593
|
-
for (const reg of registries) logger$
|
|
4594
|
-
logger$
|
|
4594
|
+
logger$4.log(`\n📋 Available registries:`);
|
|
4595
|
+
for (const reg of registries) logger$4.log(` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`);
|
|
4596
|
+
logger$4.log(`\n To use a registry, run with --registry-id <id>`);
|
|
4595
4597
|
}
|
|
4596
4598
|
} catch {}
|
|
4597
4599
|
const config = {
|
|
@@ -4600,14 +4602,14 @@ async function deployInitCommand(options) {
|
|
|
4600
4602
|
applicationId: application.applicationId
|
|
4601
4603
|
};
|
|
4602
4604
|
await updateConfig(config);
|
|
4603
|
-
logger$
|
|
4604
|
-
logger$
|
|
4605
|
-
logger$
|
|
4606
|
-
logger$
|
|
4607
|
-
logger$
|
|
4608
|
-
logger$
|
|
4609
|
-
logger$
|
|
4610
|
-
logger$
|
|
4605
|
+
logger$4.log(`\n✅ Dokploy deployment initialized!`);
|
|
4606
|
+
logger$4.log(`\n📋 Configuration:`);
|
|
4607
|
+
logger$4.log(` Project ID: ${projectId}`);
|
|
4608
|
+
logger$4.log(` Application ID: ${application.applicationId}`);
|
|
4609
|
+
logger$4.log(`\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);
|
|
4610
|
+
logger$4.log(`\n📝 Next steps:`);
|
|
4611
|
+
logger$4.log(` 1. Initialize secrets: gkm secrets:init --stage production`);
|
|
4612
|
+
logger$4.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);
|
|
4611
4613
|
return config;
|
|
4612
4614
|
}
|
|
4613
4615
|
/**
|
|
@@ -4618,32 +4620,32 @@ async function deployListCommand(options) {
|
|
|
4618
4620
|
const api = await createApi(endpoint);
|
|
4619
4621
|
const { resource } = options;
|
|
4620
4622
|
if (resource === "projects") {
|
|
4621
|
-
logger$
|
|
4623
|
+
logger$4.log(`\n📁 Projects in ${endpoint}:`);
|
|
4622
4624
|
const projects = await api.listProjects();
|
|
4623
4625
|
if (projects.length === 0) {
|
|
4624
|
-
logger$
|
|
4626
|
+
logger$4.log(" No projects found");
|
|
4625
4627
|
return;
|
|
4626
4628
|
}
|
|
4627
4629
|
for (const project of projects) {
|
|
4628
|
-
logger$
|
|
4629
|
-
if (project.description) logger$
|
|
4630
|
+
logger$4.log(`\n ${project.name} (${project.projectId})`);
|
|
4631
|
+
if (project.description) logger$4.log(` ${project.description}`);
|
|
4630
4632
|
}
|
|
4631
4633
|
} else if (resource === "registries") {
|
|
4632
|
-
logger$
|
|
4634
|
+
logger$4.log(`\n🐳 Registries in ${endpoint}:`);
|
|
4633
4635
|
const registries = await api.listRegistries();
|
|
4634
4636
|
if (registries.length === 0) {
|
|
4635
|
-
logger$
|
|
4636
|
-
logger$
|
|
4637
|
+
logger$4.log(" No registries configured");
|
|
4638
|
+
logger$4.log(" Run \"gkm registry:setup\" to configure a registry");
|
|
4637
4639
|
return;
|
|
4638
4640
|
}
|
|
4639
4641
|
const storedRegistryId = await require_credentials.getDokployRegistryId();
|
|
4640
4642
|
for (const registry of registries) {
|
|
4641
4643
|
const isDefault = registry.registryId === storedRegistryId;
|
|
4642
4644
|
const marker = isDefault ? " (default)" : "";
|
|
4643
|
-
logger$
|
|
4644
|
-
logger$
|
|
4645
|
-
logger$
|
|
4646
|
-
if (registry.imagePrefix) logger$
|
|
4645
|
+
logger$4.log(`\n ${registry.registryName}${marker} (${registry.registryId})`);
|
|
4646
|
+
logger$4.log(` URL: ${registry.registryUrl}`);
|
|
4647
|
+
logger$4.log(` Username: ${registry.username}`);
|
|
4648
|
+
if (registry.imagePrefix) logger$4.log(` Prefix: ${registry.imagePrefix}`);
|
|
4647
4649
|
}
|
|
4648
4650
|
}
|
|
4649
4651
|
}
|
|
@@ -4666,19 +4668,19 @@ function isStateProvider(value) {
|
|
|
4666
4668
|
async function createStateProvider(options) {
|
|
4667
4669
|
const { config, workspaceRoot, workspaceName } = options;
|
|
4668
4670
|
if (!config) {
|
|
4669
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
4671
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
|
|
4670
4672
|
return new LocalStateProvider(workspaceRoot);
|
|
4671
4673
|
}
|
|
4672
4674
|
if (isStateProvider(config.provider)) return config.provider;
|
|
4673
4675
|
const provider = config.provider;
|
|
4674
4676
|
if (provider === "local") {
|
|
4675
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
4677
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
|
|
4676
4678
|
return new LocalStateProvider(workspaceRoot);
|
|
4677
4679
|
}
|
|
4678
4680
|
if (provider === "ssm") {
|
|
4679
4681
|
if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
|
|
4680
|
-
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-
|
|
4681
|
-
const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-
|
|
4682
|
+
const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
|
|
4683
|
+
const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-BReQA5re.cjs"));
|
|
4682
4684
|
const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-D_uISMmJ.cjs"));
|
|
4683
4685
|
const ssmConfig = config;
|
|
4684
4686
|
const local = new LocalStateProvider(workspaceRoot);
|
|
@@ -5111,7 +5113,7 @@ async function sniffAllApps(apps, workspacePath, options = {}) {
|
|
|
5111
5113
|
|
|
5112
5114
|
//#endregion
|
|
5113
5115
|
//#region src/deploy/index.ts
|
|
5114
|
-
const logger$
|
|
5116
|
+
const logger$3 = console;
|
|
5115
5117
|
/**
|
|
5116
5118
|
* Prompt for input
|
|
5117
5119
|
*/
|
|
@@ -5183,7 +5185,7 @@ async function waitForPostgres(host, port, user, password, database, maxRetries
|
|
|
5183
5185
|
return;
|
|
5184
5186
|
} catch {
|
|
5185
5187
|
if (i < maxRetries - 1) {
|
|
5186
|
-
logger$
|
|
5188
|
+
logger$3.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);
|
|
5187
5189
|
await new Promise((r) => setTimeout(r, retryIntervalMs));
|
|
5188
5190
|
}
|
|
5189
5191
|
}
|
|
@@ -5218,12 +5220,12 @@ async function waitForPostgres(host, port, user, password, database, maxRetries
|
|
|
5218
5220
|
* ```
|
|
5219
5221
|
*/
|
|
5220
5222
|
async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
5221
|
-
logger$
|
|
5223
|
+
logger$3.log("\n🔧 Initializing database users...");
|
|
5222
5224
|
const externalPort = 5432;
|
|
5223
|
-
logger$
|
|
5225
|
+
logger$3.log(` Enabling external port ${externalPort}...`);
|
|
5224
5226
|
await api.savePostgresExternalPort(postgres.postgresId, externalPort);
|
|
5225
5227
|
await api.deployPostgres(postgres.postgresId);
|
|
5226
|
-
logger$
|
|
5228
|
+
logger$3.log(` Waiting for Postgres to be accessible at ${serverHostname}:${externalPort}...`);
|
|
5227
5229
|
await waitForPostgres(serverHostname, externalPort, postgres.databaseUser, postgres.databasePassword, postgres.databaseName);
|
|
5228
5230
|
const client = new pg.Client({
|
|
5229
5231
|
host: serverHostname,
|
|
@@ -5236,7 +5238,7 @@ async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
|
5236
5238
|
await client.connect();
|
|
5237
5239
|
for (const user of users) {
|
|
5238
5240
|
const schemaName = user.usePublicSchema ? "public" : user.name;
|
|
5239
|
-
logger$
|
|
5241
|
+
logger$3.log(` Creating user "${user.name}" with schema "${schemaName}"...`);
|
|
5240
5242
|
if (user.usePublicSchema) {
|
|
5241
5243
|
await client.query(`
|
|
5242
5244
|
DO $$ BEGIN
|
|
@@ -5271,15 +5273,15 @@ async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
|
5271
5273
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "${schemaName}" GRANT ALL ON TABLES TO "${user.name}";
|
|
5272
5274
|
`);
|
|
5273
5275
|
}
|
|
5274
|
-
logger$
|
|
5276
|
+
logger$3.log(` ✓ User "${user.name}" configured`);
|
|
5275
5277
|
}
|
|
5276
5278
|
} finally {
|
|
5277
5279
|
await client.end();
|
|
5278
5280
|
}
|
|
5279
|
-
logger$
|
|
5281
|
+
logger$3.log(" Disabling external port...");
|
|
5280
5282
|
await api.savePostgresExternalPort(postgres.postgresId, null);
|
|
5281
5283
|
await api.deployPostgres(postgres.postgresId);
|
|
5282
|
-
logger$
|
|
5284
|
+
logger$3.log(" ✓ Database users initialized");
|
|
5283
5285
|
}
|
|
5284
5286
|
/**
|
|
5285
5287
|
* Get the server hostname from the Dokploy endpoint URL
|
|
@@ -5293,24 +5295,24 @@ function getServerHostname(endpoint) {
|
|
|
5293
5295
|
* @internal Exported for testing
|
|
5294
5296
|
*/
|
|
5295
5297
|
async function provisionServices(api, projectId, environmentId, projectName, services, existingServiceIds) {
|
|
5296
|
-
logger$
|
|
5298
|
+
logger$3.log(`\n🔍 provisionServices called: services=${JSON.stringify(services)}, envId=${environmentId}`);
|
|
5297
5299
|
if (!services || !environmentId) {
|
|
5298
|
-
logger$
|
|
5300
|
+
logger$3.log(" Skipping: no services or no environmentId");
|
|
5299
5301
|
return void 0;
|
|
5300
5302
|
}
|
|
5301
5303
|
const serviceUrls = {};
|
|
5302
5304
|
const serviceIds = {};
|
|
5303
5305
|
if (services.postgres) {
|
|
5304
|
-
logger$
|
|
5306
|
+
logger$3.log("\n🐘 Checking PostgreSQL...");
|
|
5305
5307
|
const postgresName = "db";
|
|
5306
5308
|
try {
|
|
5307
5309
|
let postgres = null;
|
|
5308
5310
|
let created = false;
|
|
5309
5311
|
if (existingServiceIds?.postgresId) {
|
|
5310
|
-
logger$
|
|
5312
|
+
logger$3.log(` Using cached ID: ${existingServiceIds.postgresId}`);
|
|
5311
5313
|
postgres = await api.getPostgres(existingServiceIds.postgresId);
|
|
5312
|
-
if (postgres) logger$
|
|
5313
|
-
else logger$
|
|
5314
|
+
if (postgres) logger$3.log(` ✓ PostgreSQL found: ${postgres.postgresId}`);
|
|
5315
|
+
else logger$3.log(` ⚠ Cached ID invalid, will create new`);
|
|
5314
5316
|
}
|
|
5315
5317
|
if (!postgres) {
|
|
5316
5318
|
const databasePassword = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
@@ -5322,10 +5324,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
5322
5324
|
postgres = result.postgres;
|
|
5323
5325
|
created = result.created;
|
|
5324
5326
|
if (created) {
|
|
5325
|
-
logger$
|
|
5327
|
+
logger$3.log(` ✓ Created PostgreSQL: ${postgres.postgresId}`);
|
|
5326
5328
|
await api.deployPostgres(postgres.postgresId);
|
|
5327
|
-
logger$
|
|
5328
|
-
} else logger$
|
|
5329
|
+
logger$3.log(" ✓ PostgreSQL deployed");
|
|
5330
|
+
} else logger$3.log(` ✓ PostgreSQL already exists: ${postgres.postgresId}`);
|
|
5329
5331
|
}
|
|
5330
5332
|
serviceIds.postgresId = postgres.postgresId;
|
|
5331
5333
|
serviceUrls.DATABASE_HOST = postgres.appName;
|
|
@@ -5334,23 +5336,23 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
5334
5336
|
serviceUrls.DATABASE_USER = postgres.databaseUser;
|
|
5335
5337
|
serviceUrls.DATABASE_PASSWORD = postgres.databasePassword;
|
|
5336
5338
|
serviceUrls.DATABASE_URL = `postgresql://${postgres.databaseUser}:${postgres.databasePassword}@${postgres.appName}:5432/${postgres.databaseName}`;
|
|
5337
|
-
logger$
|
|
5339
|
+
logger$3.log(` ✓ Database credentials configured`);
|
|
5338
5340
|
} catch (error) {
|
|
5339
5341
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5340
|
-
logger$
|
|
5342
|
+
logger$3.log(` ⚠ Failed to provision PostgreSQL: ${message}`);
|
|
5341
5343
|
}
|
|
5342
5344
|
}
|
|
5343
5345
|
if (services.redis) {
|
|
5344
|
-
logger$
|
|
5346
|
+
logger$3.log("\n🔴 Checking Redis...");
|
|
5345
5347
|
const redisName = "cache";
|
|
5346
5348
|
try {
|
|
5347
5349
|
let redis = null;
|
|
5348
5350
|
let created = false;
|
|
5349
5351
|
if (existingServiceIds?.redisId) {
|
|
5350
|
-
logger$
|
|
5352
|
+
logger$3.log(` Using cached ID: ${existingServiceIds.redisId}`);
|
|
5351
5353
|
redis = await api.getRedis(existingServiceIds.redisId);
|
|
5352
|
-
if (redis) logger$
|
|
5353
|
-
else logger$
|
|
5354
|
+
if (redis) logger$3.log(` ✓ Redis found: ${redis.redisId}`);
|
|
5355
|
+
else logger$3.log(` ⚠ Cached ID invalid, will create new`);
|
|
5354
5356
|
}
|
|
5355
5357
|
if (!redis) {
|
|
5356
5358
|
const { randomBytes: randomBytes$3 } = await import("node:crypto");
|
|
@@ -5359,10 +5361,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
5359
5361
|
redis = result.redis;
|
|
5360
5362
|
created = result.created;
|
|
5361
5363
|
if (created) {
|
|
5362
|
-
logger$
|
|
5364
|
+
logger$3.log(` ✓ Created Redis: ${redis.redisId}`);
|
|
5363
5365
|
await api.deployRedis(redis.redisId);
|
|
5364
|
-
logger$
|
|
5365
|
-
} else logger$
|
|
5366
|
+
logger$3.log(" ✓ Redis deployed");
|
|
5367
|
+
} else logger$3.log(` ✓ Redis already exists: ${redis.redisId}`);
|
|
5366
5368
|
}
|
|
5367
5369
|
serviceIds.redisId = redis.redisId;
|
|
5368
5370
|
serviceUrls.REDIS_HOST = redis.appName;
|
|
@@ -5370,10 +5372,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
5370
5372
|
if (redis.databasePassword) serviceUrls.REDIS_PASSWORD = redis.databasePassword;
|
|
5371
5373
|
const password = redis.databasePassword ? `:${redis.databasePassword}@` : "";
|
|
5372
5374
|
serviceUrls.REDIS_URL = `redis://${password}${redis.appName}:6379`;
|
|
5373
|
-
logger$
|
|
5375
|
+
logger$3.log(` ✓ Redis credentials configured`);
|
|
5374
5376
|
} catch (error) {
|
|
5375
5377
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5376
|
-
logger$
|
|
5378
|
+
logger$3.log(` ⚠ Failed to provision Redis: ${message}`);
|
|
5377
5379
|
}
|
|
5378
5380
|
}
|
|
5379
5381
|
return Object.keys(serviceUrls).length > 0 ? {
|
|
@@ -5385,10 +5387,10 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
|
|
|
5385
5387
|
* Ensure Dokploy is fully configured, recovering/creating resources as needed
|
|
5386
5388
|
*/
|
|
5387
5389
|
async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
5388
|
-
logger$
|
|
5390
|
+
logger$3.log("\n🔧 Checking Dokploy setup...");
|
|
5389
5391
|
let creds = await require_credentials.getDokployCredentials();
|
|
5390
5392
|
if (!creds) {
|
|
5391
|
-
logger$
|
|
5393
|
+
logger$3.log("\n📋 Dokploy credentials not found. Let's set them up.");
|
|
5392
5394
|
const endpoint = await prompt("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
5393
5395
|
const normalizedEndpoint = endpoint.replace(/\/$/, "");
|
|
5394
5396
|
try {
|
|
@@ -5396,9 +5398,9 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
5396
5398
|
} catch {
|
|
5397
5399
|
throw new Error("Invalid URL format");
|
|
5398
5400
|
}
|
|
5399
|
-
logger$
|
|
5401
|
+
logger$3.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
|
|
5400
5402
|
const token = await prompt("API Token: ", true);
|
|
5401
|
-
logger$
|
|
5403
|
+
logger$3.log("\nValidating credentials...");
|
|
5402
5404
|
const isValid = await validateDokployToken(normalizedEndpoint, token);
|
|
5403
5405
|
if (!isValid) throw new Error("Invalid credentials. Please check your token.");
|
|
5404
5406
|
await require_credentials.storeDokployCredentials(token, normalizedEndpoint);
|
|
@@ -5406,7 +5408,7 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
5406
5408
|
token,
|
|
5407
5409
|
endpoint: normalizedEndpoint
|
|
5408
5410
|
};
|
|
5409
|
-
logger$
|
|
5411
|
+
logger$3.log("✓ Credentials saved");
|
|
5410
5412
|
}
|
|
5411
5413
|
const api = new require_dokploy_api.DokployApi({
|
|
5412
5414
|
baseUrl: creds.endpoint,
|
|
@@ -5414,20 +5416,20 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
5414
5416
|
});
|
|
5415
5417
|
const existingConfig = config.providers?.dokploy;
|
|
5416
5418
|
if (existingConfig && typeof existingConfig !== "boolean" && existingConfig.applicationId && existingConfig.projectId) {
|
|
5417
|
-
logger$
|
|
5419
|
+
logger$3.log("✓ Dokploy config found in gkm.config.ts");
|
|
5418
5420
|
try {
|
|
5419
5421
|
const projectDetails = await api.getProject(existingConfig.projectId);
|
|
5420
|
-
logger$
|
|
5422
|
+
logger$3.log("✓ Project verified");
|
|
5421
5423
|
const storedRegistryId = existingConfig.registryId ?? await require_credentials.getDokployRegistryId();
|
|
5422
5424
|
const environments = projectDetails.environments ?? [];
|
|
5423
5425
|
let environment = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
5424
5426
|
if (!environment) {
|
|
5425
|
-
logger$
|
|
5427
|
+
logger$3.log(` Creating "${stage}" environment...`);
|
|
5426
5428
|
environment = await api.createEnvironment(existingConfig.projectId, stage);
|
|
5427
|
-
logger$
|
|
5429
|
+
logger$3.log(` ✓ Created environment: ${environment.environmentId}`);
|
|
5428
5430
|
}
|
|
5429
5431
|
const environmentId$1 = environment.environmentId;
|
|
5430
|
-
logger$
|
|
5432
|
+
logger$3.log(` Services config: ${JSON.stringify(services)}, envId: ${environmentId$1}`);
|
|
5431
5433
|
const provisionResult$1 = await provisionServices(api, existingConfig.projectId, environmentId$1, dockerConfig.appName, services, void 0);
|
|
5432
5434
|
return {
|
|
5433
5435
|
config: {
|
|
@@ -5440,97 +5442,97 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
5440
5442
|
serviceUrls: provisionResult$1?.serviceUrls
|
|
5441
5443
|
};
|
|
5442
5444
|
} catch {
|
|
5443
|
-
logger$
|
|
5445
|
+
logger$3.log("⚠ Project not found, will recover...");
|
|
5444
5446
|
}
|
|
5445
5447
|
}
|
|
5446
|
-
logger$
|
|
5448
|
+
logger$3.log("\n📁 Looking for project...");
|
|
5447
5449
|
const projectName = dockerConfig.projectName;
|
|
5448
5450
|
const projects = await api.listProjects();
|
|
5449
5451
|
let project = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
5450
5452
|
let environmentId;
|
|
5451
5453
|
if (project) {
|
|
5452
|
-
logger$
|
|
5454
|
+
logger$3.log(` Found existing project: ${project.name} (${project.projectId})`);
|
|
5453
5455
|
const projectDetails = await api.getProject(project.projectId);
|
|
5454
5456
|
const environments = projectDetails.environments ?? [];
|
|
5455
5457
|
const matchingEnv = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
5456
5458
|
if (matchingEnv) {
|
|
5457
5459
|
environmentId = matchingEnv.environmentId;
|
|
5458
|
-
logger$
|
|
5460
|
+
logger$3.log(` Using environment: ${matchingEnv.name}`);
|
|
5459
5461
|
} else {
|
|
5460
|
-
logger$
|
|
5462
|
+
logger$3.log(` Creating "${stage}" environment...`);
|
|
5461
5463
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
5462
5464
|
environmentId = env.environmentId;
|
|
5463
|
-
logger$
|
|
5465
|
+
logger$3.log(` ✓ Created environment: ${stage}`);
|
|
5464
5466
|
}
|
|
5465
5467
|
} else {
|
|
5466
|
-
logger$
|
|
5468
|
+
logger$3.log(` Creating project: ${projectName}`);
|
|
5467
5469
|
const result = await api.createProject(projectName);
|
|
5468
5470
|
project = result.project;
|
|
5469
5471
|
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
5470
|
-
logger$
|
|
5472
|
+
logger$3.log(` Creating "${stage}" environment...`);
|
|
5471
5473
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
5472
5474
|
environmentId = env.environmentId;
|
|
5473
5475
|
} else environmentId = result.environment.environmentId;
|
|
5474
|
-
logger$
|
|
5475
|
-
logger$
|
|
5476
|
+
logger$3.log(` ✓ Created project: ${project.projectId}`);
|
|
5477
|
+
logger$3.log(` ✓ Using environment: ${stage}`);
|
|
5476
5478
|
}
|
|
5477
|
-
logger$
|
|
5479
|
+
logger$3.log("\n📦 Looking for application...");
|
|
5478
5480
|
const appName = dockerConfig.appName;
|
|
5479
5481
|
let applicationId;
|
|
5480
5482
|
if (existingConfig && typeof existingConfig !== "boolean" && existingConfig.applicationId) {
|
|
5481
5483
|
applicationId = existingConfig.applicationId;
|
|
5482
|
-
logger$
|
|
5484
|
+
logger$3.log(` Using application from config: ${applicationId}`);
|
|
5483
5485
|
} else {
|
|
5484
|
-
logger$
|
|
5486
|
+
logger$3.log(` Creating application: ${appName}`);
|
|
5485
5487
|
const app = await api.createApplication(appName, project.projectId, environmentId);
|
|
5486
5488
|
applicationId = app.applicationId;
|
|
5487
|
-
logger$
|
|
5489
|
+
logger$3.log(` ✓ Created application: ${applicationId}`);
|
|
5488
5490
|
}
|
|
5489
|
-
logger$
|
|
5491
|
+
logger$3.log("\n🐳 Checking registry...");
|
|
5490
5492
|
let registryId = await require_credentials.getDokployRegistryId();
|
|
5491
5493
|
if (registryId) try {
|
|
5492
5494
|
const registry = await api.getRegistry(registryId);
|
|
5493
|
-
logger$
|
|
5495
|
+
logger$3.log(` Using registry: ${registry.registryName}`);
|
|
5494
5496
|
} catch {
|
|
5495
|
-
logger$
|
|
5497
|
+
logger$3.log(" ⚠ Stored registry not found, clearing...");
|
|
5496
5498
|
registryId = void 0;
|
|
5497
5499
|
await require_credentials.storeDokployRegistryId("");
|
|
5498
5500
|
}
|
|
5499
5501
|
if (!registryId) {
|
|
5500
5502
|
const registries = await api.listRegistries();
|
|
5501
5503
|
if (registries.length === 0) if (dockerConfig.registry) {
|
|
5502
|
-
logger$
|
|
5503
|
-
logger$
|
|
5504
|
+
logger$3.log(" No registries found in Dokploy. Let's create one.");
|
|
5505
|
+
logger$3.log(` Registry URL: ${dockerConfig.registry}`);
|
|
5504
5506
|
const username = await prompt("Registry username: ");
|
|
5505
5507
|
const password = await prompt("Registry password/token: ", true);
|
|
5506
5508
|
const registry = await api.createRegistry("Default Registry", dockerConfig.registry, username, password);
|
|
5507
5509
|
registryId = registry.registryId;
|
|
5508
5510
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
5509
|
-
logger$
|
|
5510
|
-
} else logger$
|
|
5511
|
+
logger$3.log(` ✓ Registry created: ${registryId}`);
|
|
5512
|
+
} else logger$3.log(" ⚠ No registry configured. Set docker.registry in gkm.config.ts");
|
|
5511
5513
|
else {
|
|
5512
|
-
logger$
|
|
5514
|
+
logger$3.log(" Available registries:");
|
|
5513
5515
|
registries.forEach((reg, i) => {
|
|
5514
|
-
logger$
|
|
5516
|
+
logger$3.log(` ${i + 1}. ${reg.registryName} (${reg.registryUrl})`);
|
|
5515
5517
|
});
|
|
5516
|
-
if (dockerConfig.registry) logger$
|
|
5518
|
+
if (dockerConfig.registry) logger$3.log(` ${registries.length + 1}. Create new registry`);
|
|
5517
5519
|
const maxOption = dockerConfig.registry ? registries.length + 1 : registries.length;
|
|
5518
5520
|
const selection = await prompt(` Select registry (1-${maxOption}): `);
|
|
5519
5521
|
const index = parseInt(selection, 10) - 1;
|
|
5520
5522
|
if (index >= 0 && index < registries.length) {
|
|
5521
5523
|
registryId = registries[index].registryId;
|
|
5522
5524
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
5523
|
-
logger$
|
|
5525
|
+
logger$3.log(` ✓ Selected: ${registries[index].registryName}`);
|
|
5524
5526
|
} else if (dockerConfig.registry && index === registries.length) {
|
|
5525
|
-
logger$
|
|
5526
|
-
logger$
|
|
5527
|
+
logger$3.log(`\n Creating new registry...`);
|
|
5528
|
+
logger$3.log(` Registry URL: ${dockerConfig.registry}`);
|
|
5527
5529
|
const username = await prompt(" Registry username: ");
|
|
5528
5530
|
const password = await prompt(" Registry password/token: ", true);
|
|
5529
5531
|
const registry = await api.createRegistry(dockerConfig.registry.replace(/^https?:\/\//, ""), dockerConfig.registry, username, password);
|
|
5530
5532
|
registryId = registry.registryId;
|
|
5531
5533
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
5532
|
-
logger$
|
|
5533
|
-
} else logger$
|
|
5534
|
+
logger$3.log(` ✓ Registry created: ${registryId}`);
|
|
5535
|
+
} else logger$3.log(" ⚠ Invalid selection, skipping registry setup");
|
|
5534
5536
|
}
|
|
5535
5537
|
}
|
|
5536
5538
|
const dokployConfig = {
|
|
@@ -5540,10 +5542,10 @@ async function ensureDokploySetup(config, dockerConfig, stage, services) {
|
|
|
5540
5542
|
registryId: registryId ?? void 0
|
|
5541
5543
|
};
|
|
5542
5544
|
await updateConfig(dokployConfig);
|
|
5543
|
-
logger$
|
|
5544
|
-
logger$
|
|
5545
|
-
logger$
|
|
5546
|
-
if (registryId) logger$
|
|
5545
|
+
logger$3.log("\n✅ Dokploy setup complete!");
|
|
5546
|
+
logger$3.log(` Project: ${project.projectId}`);
|
|
5547
|
+
logger$3.log(` Application: ${applicationId}`);
|
|
5548
|
+
if (registryId) logger$3.log(` Registry: ${registryId}`);
|
|
5547
5549
|
const provisionResult = await provisionServices(api, project.projectId, environmentId, dockerConfig.appName, services, void 0);
|
|
5548
5550
|
return {
|
|
5549
5551
|
config: dokployConfig,
|
|
@@ -5574,45 +5576,45 @@ function generateTag(stage) {
|
|
|
5574
5576
|
async function workspaceDeployCommand(workspace, options) {
|
|
5575
5577
|
const { provider, stage, tag, apps: selectedApps } = options;
|
|
5576
5578
|
if (provider !== "dokploy") throw new Error(`Workspace deployment only supports Dokploy. Got: ${provider}`);
|
|
5577
|
-
logger$
|
|
5578
|
-
logger$
|
|
5579
|
+
logger$3.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
|
|
5580
|
+
logger$3.log(` Stage: ${stage}`);
|
|
5579
5581
|
const imageTag = tag ?? generateTag(stage);
|
|
5580
|
-
logger$
|
|
5582
|
+
logger$3.log(` Tag: ${imageTag}`);
|
|
5581
5583
|
const buildOrder = require_workspace.getAppBuildOrder(workspace);
|
|
5582
5584
|
let appsToDeployNames = buildOrder;
|
|
5583
5585
|
if (selectedApps && selectedApps.length > 0) {
|
|
5584
5586
|
const invalidApps = selectedApps.filter((name$1) => !workspace.apps[name$1]);
|
|
5585
5587
|
if (invalidApps.length > 0) throw new Error(`Unknown apps: ${invalidApps.join(", ")}\nAvailable apps: ${Object.keys(workspace.apps).join(", ")}`);
|
|
5586
5588
|
appsToDeployNames = buildOrder.filter((name$1) => selectedApps.includes(name$1));
|
|
5587
|
-
logger$
|
|
5588
|
-
} else logger$
|
|
5589
|
+
logger$3.log(` Deploying apps: ${appsToDeployNames.join(", ")}`);
|
|
5590
|
+
} else logger$3.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
|
|
5589
5591
|
const dokployApps = appsToDeployNames.filter((name$1) => {
|
|
5590
5592
|
const app = workspace.apps[name$1];
|
|
5591
5593
|
const target = app.resolvedDeployTarget;
|
|
5592
5594
|
if (!require_workspace.isDeployTargetSupported(target)) {
|
|
5593
|
-
logger$
|
|
5595
|
+
logger$3.log(` ⚠️ Skipping ${name$1}: ${require_workspace.getDeployTargetError(target, name$1)}`);
|
|
5594
5596
|
return false;
|
|
5595
5597
|
}
|
|
5596
5598
|
return true;
|
|
5597
5599
|
});
|
|
5598
5600
|
if (dokployApps.length === 0) throw new Error("No apps to deploy. All selected apps have unsupported deploy targets.");
|
|
5599
5601
|
appsToDeployNames = dokployApps;
|
|
5600
|
-
logger$
|
|
5602
|
+
logger$3.log("\n🔐 Loading secrets and analyzing environment requirements...");
|
|
5601
5603
|
const stageSecrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
5602
5604
|
if (!stageSecrets) {
|
|
5603
|
-
logger$
|
|
5604
|
-
logger$
|
|
5605
|
+
logger$3.log(` ⚠️ No secrets found for stage "${stage}"`);
|
|
5606
|
+
logger$3.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
|
|
5605
5607
|
}
|
|
5606
5608
|
const sniffedApps = await sniffAllApps(workspace.apps, workspace.root);
|
|
5607
5609
|
const encryptedSecrets = stageSecrets ? prepareSecretsForAllApps(stageSecrets, sniffedApps) : /* @__PURE__ */ new Map();
|
|
5608
5610
|
if (stageSecrets) {
|
|
5609
5611
|
const report = generateSecretsReport(encryptedSecrets, sniffedApps);
|
|
5610
|
-
if (report.appsWithSecrets.length > 0) logger$
|
|
5611
|
-
if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$
|
|
5612
|
+
if (report.appsWithSecrets.length > 0) logger$3.log(` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(", ")}`);
|
|
5613
|
+
if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$3.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(", ")}`);
|
|
5612
5614
|
}
|
|
5613
5615
|
let creds = await require_credentials.getDokployCredentials();
|
|
5614
5616
|
if (!creds) {
|
|
5615
|
-
logger$
|
|
5617
|
+
logger$3.log("\n📋 Dokploy credentials not found. Let's set them up.");
|
|
5616
5618
|
const endpoint = await prompt("Dokploy URL (e.g., https://dokploy.example.com): ");
|
|
5617
5619
|
const normalizedEndpoint = endpoint.replace(/\/$/, "");
|
|
5618
5620
|
try {
|
|
@@ -5620,9 +5622,9 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5620
5622
|
} catch {
|
|
5621
5623
|
throw new Error("Invalid URL format");
|
|
5622
5624
|
}
|
|
5623
|
-
logger$
|
|
5625
|
+
logger$3.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
|
|
5624
5626
|
const token = await prompt("API Token: ", true);
|
|
5625
|
-
logger$
|
|
5627
|
+
logger$3.log("\nValidating credentials...");
|
|
5626
5628
|
const isValid = await validateDokployToken(normalizedEndpoint, token);
|
|
5627
5629
|
if (!isValid) throw new Error("Invalid credentials. Please check your token.");
|
|
5628
5630
|
await require_credentials.storeDokployCredentials(token, normalizedEndpoint);
|
|
@@ -5630,43 +5632,43 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5630
5632
|
token,
|
|
5631
5633
|
endpoint: normalizedEndpoint
|
|
5632
5634
|
};
|
|
5633
|
-
logger$
|
|
5635
|
+
logger$3.log("✓ Credentials saved");
|
|
5634
5636
|
}
|
|
5635
5637
|
const api = new require_dokploy_api.DokployApi({
|
|
5636
5638
|
baseUrl: creds.endpoint,
|
|
5637
5639
|
token: creds.token
|
|
5638
5640
|
});
|
|
5639
|
-
logger$
|
|
5641
|
+
logger$3.log("\n📁 Setting up Dokploy project...");
|
|
5640
5642
|
const projectName = workspace.name;
|
|
5641
5643
|
const projects = await api.listProjects();
|
|
5642
5644
|
let project = projects.find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
5643
5645
|
let environmentId;
|
|
5644
5646
|
if (project) {
|
|
5645
|
-
logger$
|
|
5647
|
+
logger$3.log(` Found existing project: ${project.name}`);
|
|
5646
5648
|
const projectDetails = await api.getProject(project.projectId);
|
|
5647
5649
|
const environments = projectDetails.environments ?? [];
|
|
5648
5650
|
const matchingEnv = environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
5649
5651
|
if (matchingEnv) {
|
|
5650
5652
|
environmentId = matchingEnv.environmentId;
|
|
5651
|
-
logger$
|
|
5653
|
+
logger$3.log(` Using environment: ${matchingEnv.name}`);
|
|
5652
5654
|
} else {
|
|
5653
|
-
logger$
|
|
5655
|
+
logger$3.log(` Creating "${stage}" environment...`);
|
|
5654
5656
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
5655
5657
|
environmentId = env.environmentId;
|
|
5656
|
-
logger$
|
|
5658
|
+
logger$3.log(` ✓ Created environment: ${stage}`);
|
|
5657
5659
|
}
|
|
5658
5660
|
} else {
|
|
5659
|
-
logger$
|
|
5661
|
+
logger$3.log(` Creating project: ${projectName}`);
|
|
5660
5662
|
const result = await api.createProject(projectName);
|
|
5661
5663
|
project = result.project;
|
|
5662
5664
|
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
5663
|
-
logger$
|
|
5665
|
+
logger$3.log(` Creating "${stage}" environment...`);
|
|
5664
5666
|
const env = await api.createEnvironment(project.projectId, stage);
|
|
5665
5667
|
environmentId = env.environmentId;
|
|
5666
5668
|
} else environmentId = result.environment.environmentId;
|
|
5667
|
-
logger$
|
|
5669
|
+
logger$3.log(` ✓ Created project: ${project.projectId}`);
|
|
5668
5670
|
}
|
|
5669
|
-
logger$
|
|
5671
|
+
logger$3.log("\n📋 Loading deploy state...");
|
|
5670
5672
|
const stateProvider = await createStateProvider({
|
|
5671
5673
|
config: workspace.state,
|
|
5672
5674
|
workspaceRoot: workspace.root,
|
|
@@ -5674,27 +5676,27 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5674
5676
|
});
|
|
5675
5677
|
let state = await stateProvider.read(stage);
|
|
5676
5678
|
if (state) {
|
|
5677
|
-
logger$
|
|
5679
|
+
logger$3.log(` Found existing state for stage "${stage}"`);
|
|
5678
5680
|
if (state.projectId !== project.projectId) {
|
|
5679
|
-
logger$
|
|
5681
|
+
logger$3.log(` ⚠ Project ID changed, updating state`);
|
|
5680
5682
|
state.projectId = project.projectId;
|
|
5681
5683
|
}
|
|
5682
5684
|
if (state.environmentId !== environmentId) {
|
|
5683
|
-
logger$
|
|
5685
|
+
logger$3.log(` ⚠ Environment ID changed, updating state`);
|
|
5684
5686
|
state.environmentId = environmentId;
|
|
5685
5687
|
}
|
|
5686
5688
|
} else {
|
|
5687
|
-
logger$
|
|
5689
|
+
logger$3.log(` Creating new state for stage "${stage}"`);
|
|
5688
5690
|
state = createEmptyState(stage, project.projectId, environmentId);
|
|
5689
5691
|
}
|
|
5690
|
-
logger$
|
|
5692
|
+
logger$3.log("\n🐳 Checking registry...");
|
|
5691
5693
|
let registryId = await require_credentials.getDokployRegistryId();
|
|
5692
5694
|
const registry = workspace.deploy.dokploy?.registry;
|
|
5693
5695
|
if (registryId) try {
|
|
5694
5696
|
const reg = await api.getRegistry(registryId);
|
|
5695
|
-
logger$
|
|
5697
|
+
logger$3.log(` Using registry: ${reg.registryName}`);
|
|
5696
5698
|
} catch {
|
|
5697
|
-
logger$
|
|
5699
|
+
logger$3.log(" ⚠ Stored registry not found, clearing...");
|
|
5698
5700
|
registryId = void 0;
|
|
5699
5701
|
await require_credentials.storeDokployRegistryId("");
|
|
5700
5702
|
}
|
|
@@ -5703,17 +5705,17 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5703
5705
|
if (registries.length > 0) {
|
|
5704
5706
|
registryId = registries[0].registryId;
|
|
5705
5707
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
5706
|
-
logger$
|
|
5708
|
+
logger$3.log(` Using registry: ${registries[0].registryName}`);
|
|
5707
5709
|
} else if (registry) {
|
|
5708
|
-
logger$
|
|
5709
|
-
logger$
|
|
5710
|
+
logger$3.log(" No registries found in Dokploy. Let's create one.");
|
|
5711
|
+
logger$3.log(` Registry URL: ${registry}`);
|
|
5710
5712
|
const username = await prompt("Registry username: ");
|
|
5711
5713
|
const password = await prompt("Registry password/token: ", true);
|
|
5712
5714
|
const reg = await api.createRegistry("Default Registry", registry, username, password);
|
|
5713
5715
|
registryId = reg.registryId;
|
|
5714
5716
|
await require_credentials.storeDokployRegistryId(registryId);
|
|
5715
|
-
logger$
|
|
5716
|
-
} else logger$
|
|
5717
|
+
logger$3.log(` ✓ Registry created: ${registryId}`);
|
|
5718
|
+
} else logger$3.log(" ⚠ No registry configured. Set deploy.dokploy.registry in workspace config");
|
|
5717
5719
|
}
|
|
5718
5720
|
const services = workspace.services;
|
|
5719
5721
|
const dockerServices = {
|
|
@@ -5723,7 +5725,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5723
5725
|
let provisionedPostgres = null;
|
|
5724
5726
|
let provisionedRedis = null;
|
|
5725
5727
|
if (dockerServices.postgres || dockerServices.redis) {
|
|
5726
|
-
logger$
|
|
5728
|
+
logger$3.log("\n🔧 Provisioning infrastructure services...");
|
|
5727
5729
|
const existingServiceIds = {
|
|
5728
5730
|
postgresId: getPostgresId(state),
|
|
5729
5731
|
redisId: getRedisId(state)
|
|
@@ -5749,13 +5751,13 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5749
5751
|
return requirements?.requiredEnvVars.includes("DATABASE_URL");
|
|
5750
5752
|
});
|
|
5751
5753
|
if (appsNeedingDb.length > 0) {
|
|
5752
|
-
logger$
|
|
5753
|
-
logger$
|
|
5754
|
+
logger$3.log(`\n🔐 Setting up per-app database credentials...`);
|
|
5755
|
+
logger$3.log(` Apps needing DATABASE_URL: ${appsNeedingDb.join(", ")}`);
|
|
5754
5756
|
const existingCredentials = getAllAppCredentials(state);
|
|
5755
5757
|
const usersToCreate = [];
|
|
5756
5758
|
for (const appName of appsNeedingDb) {
|
|
5757
5759
|
let credentials = existingCredentials[appName];
|
|
5758
|
-
if (credentials) logger$
|
|
5760
|
+
if (credentials) logger$3.log(` ${appName}: Using existing credentials from state`);
|
|
5759
5761
|
else {
|
|
5760
5762
|
const password = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
5761
5763
|
credentials = {
|
|
@@ -5763,7 +5765,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5763
5765
|
dbPassword: password
|
|
5764
5766
|
};
|
|
5765
5767
|
setAppCredentials(state, appName, credentials);
|
|
5766
|
-
logger$
|
|
5768
|
+
logger$3.log(` ${appName}: Generated new credentials`);
|
|
5767
5769
|
}
|
|
5768
5770
|
perAppDbCredentials.set(appName, credentials);
|
|
5769
5771
|
usersToCreate.push({
|
|
@@ -5777,8 +5779,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5777
5779
|
}
|
|
5778
5780
|
}
|
|
5779
5781
|
if (workspace.deploy?.backups && provisionedPostgres) {
|
|
5780
|
-
logger$
|
|
5781
|
-
const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-
|
|
5782
|
+
logger$3.log("\n💾 Provisioning backup destination...");
|
|
5783
|
+
const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-C8VK63I-.cjs"));
|
|
5782
5784
|
const backupState = await provisionBackupDestination({
|
|
5783
5785
|
api,
|
|
5784
5786
|
projectId: project.projectId,
|
|
@@ -5786,13 +5788,13 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5786
5788
|
stage,
|
|
5787
5789
|
config: workspace.deploy.backups,
|
|
5788
5790
|
existingState: getBackupState(state),
|
|
5789
|
-
logger: logger$
|
|
5791
|
+
logger: logger$3
|
|
5790
5792
|
});
|
|
5791
5793
|
setBackupState(state, backupState);
|
|
5792
5794
|
if (!backupState.postgresBackupId) {
|
|
5793
5795
|
const backupSchedule = workspace.deploy.backups.schedule ?? "0 2 * * *";
|
|
5794
5796
|
const backupRetention = workspace.deploy.backups.retention ?? 30;
|
|
5795
|
-
logger$
|
|
5797
|
+
logger$3.log(" Creating postgres backup schedule...");
|
|
5796
5798
|
const backup = await api.createPostgresBackup({
|
|
5797
5799
|
schedule: backupSchedule,
|
|
5798
5800
|
prefix: `${stage}/postgres`,
|
|
@@ -5803,8 +5805,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5803
5805
|
keepLatestCount: backupRetention
|
|
5804
5806
|
});
|
|
5805
5807
|
setPostgresBackupId(state, backup.backupId);
|
|
5806
|
-
logger$
|
|
5807
|
-
} else logger$
|
|
5808
|
+
logger$3.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
|
|
5809
|
+
} else logger$3.log(" ✓ Using existing postgres backup schedule");
|
|
5808
5810
|
}
|
|
5809
5811
|
const publicUrls = {};
|
|
5810
5812
|
const results = [];
|
|
@@ -5819,25 +5821,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5819
5821
|
frontendUrls.push(`https://${hostname}`);
|
|
5820
5822
|
}
|
|
5821
5823
|
if (backendApps.length > 0) {
|
|
5822
|
-
logger$
|
|
5824
|
+
logger$3.log("\n📦 PHASE 1: Deploying backend applications...");
|
|
5823
5825
|
for (const appName of backendApps) {
|
|
5824
5826
|
const app = workspace.apps[appName];
|
|
5825
|
-
logger$
|
|
5827
|
+
logger$3.log(`\n ⚙️ Deploying ${appName}...`);
|
|
5826
5828
|
try {
|
|
5827
5829
|
const dokployAppName = appName;
|
|
5828
5830
|
let application = null;
|
|
5829
5831
|
const cachedAppId = getApplicationId(state, appName);
|
|
5830
5832
|
if (cachedAppId) {
|
|
5831
|
-
logger$
|
|
5833
|
+
logger$3.log(` Using cached ID: ${cachedAppId}`);
|
|
5832
5834
|
application = await api.getApplication(cachedAppId);
|
|
5833
|
-
if (application) logger$
|
|
5834
|
-
else logger$
|
|
5835
|
+
if (application) logger$3.log(` ✓ Application found: ${application.applicationId}`);
|
|
5836
|
+
else logger$3.log(` ⚠ Cached ID invalid, will create new`);
|
|
5835
5837
|
}
|
|
5836
5838
|
if (!application) {
|
|
5837
5839
|
const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
|
|
5838
5840
|
application = result.application;
|
|
5839
|
-
if (result.created) logger$
|
|
5840
|
-
else logger$
|
|
5841
|
+
if (result.created) logger$3.log(` Created application: ${application.applicationId}`);
|
|
5842
|
+
else logger$3.log(` Found existing application: ${application.applicationId}`);
|
|
5841
5843
|
}
|
|
5842
5844
|
setApplicationId(state, appName, application.applicationId);
|
|
5843
5845
|
const appSecrets = encryptedSecrets.get(appName);
|
|
@@ -5845,11 +5847,11 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5845
5847
|
if (appSecrets && appSecrets.secretCount > 0) {
|
|
5846
5848
|
buildArgs.push(`GKM_ENCRYPTED_CREDENTIALS=${appSecrets.payload.encrypted}`);
|
|
5847
5849
|
buildArgs.push(`GKM_CREDENTIALS_IV=${appSecrets.payload.iv}`);
|
|
5848
|
-
logger$
|
|
5850
|
+
logger$3.log(` Encrypted ${appSecrets.secretCount} secrets`);
|
|
5849
5851
|
}
|
|
5850
5852
|
const imageName = `${workspace.name}-${appName}`;
|
|
5851
5853
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
5852
|
-
logger$
|
|
5854
|
+
logger$3.log(` Building Docker image: ${imageRef}`);
|
|
5853
5855
|
await deployDocker({
|
|
5854
5856
|
stage,
|
|
5855
5857
|
tag: imageTag,
|
|
@@ -5899,10 +5901,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5899
5901
|
const { valid, missing, resolved } = validateEnvVars(requiredVars, envContext);
|
|
5900
5902
|
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
5901
5903
|
const envVars = Object.entries(resolved).map(([key, value]) => `${key}=${value}`);
|
|
5902
|
-
if (Object.keys(resolved).length > 0) logger$
|
|
5904
|
+
if (Object.keys(resolved).length > 0) logger$3.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
|
|
5903
5905
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
5904
5906
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
5905
|
-
logger$
|
|
5907
|
+
logger$3.log(` Deploying to Dokploy...`);
|
|
5906
5908
|
await api.deployApplication(application.applicationId);
|
|
5907
5909
|
const existingDomains = await api.getDomainsByApplicationId(application.applicationId);
|
|
5908
5910
|
const existingDomain = existingDomains.find((d) => d.host === backendHost);
|
|
@@ -5910,7 +5912,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5910
5912
|
appHostnames.set(appName, backendHost);
|
|
5911
5913
|
appDomainIds.set(appName, existingDomain.domainId);
|
|
5912
5914
|
publicUrls[appName] = `https://${backendHost}`;
|
|
5913
|
-
logger$
|
|
5915
|
+
logger$3.log(` ✓ Domain: https://${backendHost} (existing)`);
|
|
5914
5916
|
} else try {
|
|
5915
5917
|
const domain = await api.createDomain({
|
|
5916
5918
|
host: backendHost,
|
|
@@ -5922,10 +5924,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5922
5924
|
appHostnames.set(appName, backendHost);
|
|
5923
5925
|
appDomainIds.set(appName, domain.domainId);
|
|
5924
5926
|
publicUrls[appName] = `https://${backendHost}`;
|
|
5925
|
-
logger$
|
|
5927
|
+
logger$3.log(` ✓ Domain: https://${backendHost} (created)`);
|
|
5926
5928
|
} catch (domainError) {
|
|
5927
5929
|
const message = domainError instanceof Error ? domainError.message : "Unknown error";
|
|
5928
|
-
logger$
|
|
5930
|
+
logger$3.log(` ⚠ Domain creation failed: ${message}`);
|
|
5929
5931
|
appHostnames.set(appName, backendHost);
|
|
5930
5932
|
publicUrls[appName] = `https://${backendHost}`;
|
|
5931
5933
|
}
|
|
@@ -5936,10 +5938,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5936
5938
|
applicationId: application.applicationId,
|
|
5937
5939
|
imageRef
|
|
5938
5940
|
});
|
|
5939
|
-
logger$
|
|
5941
|
+
logger$3.log(` ✓ ${appName} deployed successfully`);
|
|
5940
5942
|
} catch (error) {
|
|
5941
5943
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5942
|
-
logger$
|
|
5944
|
+
logger$3.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
5943
5945
|
results.push({
|
|
5944
5946
|
appName,
|
|
5945
5947
|
type: app.type,
|
|
@@ -5951,25 +5953,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5951
5953
|
}
|
|
5952
5954
|
}
|
|
5953
5955
|
if (frontendApps.length > 0) {
|
|
5954
|
-
logger$
|
|
5956
|
+
logger$3.log("\n🌐 PHASE 2: Deploying frontend applications...");
|
|
5955
5957
|
for (const appName of frontendApps) {
|
|
5956
5958
|
const app = workspace.apps[appName];
|
|
5957
|
-
logger$
|
|
5959
|
+
logger$3.log(`\n 🌐 Deploying ${appName}...`);
|
|
5958
5960
|
try {
|
|
5959
5961
|
const dokployAppName = appName;
|
|
5960
5962
|
let application = null;
|
|
5961
5963
|
const cachedAppId = getApplicationId(state, appName);
|
|
5962
5964
|
if (cachedAppId) {
|
|
5963
|
-
logger$
|
|
5965
|
+
logger$3.log(` Using cached ID: ${cachedAppId}`);
|
|
5964
5966
|
application = await api.getApplication(cachedAppId);
|
|
5965
|
-
if (application) logger$
|
|
5966
|
-
else logger$
|
|
5967
|
+
if (application) logger$3.log(` ✓ Application found: ${application.applicationId}`);
|
|
5968
|
+
else logger$3.log(` ⚠ Cached ID invalid, will create new`);
|
|
5967
5969
|
}
|
|
5968
5970
|
if (!application) {
|
|
5969
5971
|
const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
|
|
5970
5972
|
application = result.application;
|
|
5971
|
-
if (result.created) logger$
|
|
5972
|
-
else logger$
|
|
5973
|
+
if (result.created) logger$3.log(` Created application: ${application.applicationId}`);
|
|
5974
|
+
else logger$3.log(` Found existing application: ${application.applicationId}`);
|
|
5973
5975
|
}
|
|
5974
5976
|
setApplicationId(state, appName, application.applicationId);
|
|
5975
5977
|
const dependencyUrls = {};
|
|
@@ -5991,17 +5993,17 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5991
5993
|
const sniffedVars = sniffedApps.get(appName)?.requiredEnvVars ?? [];
|
|
5992
5994
|
const { valid, missing, resolved } = validateEnvVars(sniffedVars, envContext);
|
|
5993
5995
|
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
5994
|
-
if (Object.keys(resolved).length > 0) logger$
|
|
5996
|
+
if (Object.keys(resolved).length > 0) logger$3.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
|
|
5995
5997
|
const buildArgs = [];
|
|
5996
5998
|
const publicUrlArgNames = [];
|
|
5997
5999
|
for (const [key, value] of Object.entries(resolved)) if (key.startsWith("NEXT_PUBLIC_")) {
|
|
5998
6000
|
buildArgs.push(`${key}=${value}`);
|
|
5999
6001
|
publicUrlArgNames.push(key);
|
|
6000
6002
|
}
|
|
6001
|
-
if (buildArgs.length > 0) logger$
|
|
6003
|
+
if (buildArgs.length > 0) logger$3.log(` Build args: ${publicUrlArgNames.join(", ")}`);
|
|
6002
6004
|
const imageName = `${workspace.name}-${appName}`;
|
|
6003
6005
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
6004
|
-
logger$
|
|
6006
|
+
logger$3.log(` Building Docker image: ${imageRef}`);
|
|
6005
6007
|
await deployDocker({
|
|
6006
6008
|
stage,
|
|
6007
6009
|
tag: imageTag,
|
|
@@ -6022,7 +6024,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6022
6024
|
for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
|
|
6023
6025
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
6024
6026
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
6025
|
-
logger$
|
|
6027
|
+
logger$3.log(` Deploying to Dokploy...`);
|
|
6026
6028
|
await api.deployApplication(application.applicationId);
|
|
6027
6029
|
const existingFrontendDomains = await api.getDomainsByApplicationId(application.applicationId);
|
|
6028
6030
|
const existingFrontendDomain = existingFrontendDomains.find((d) => d.host === frontendHost);
|
|
@@ -6030,7 +6032,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6030
6032
|
appHostnames.set(appName, frontendHost);
|
|
6031
6033
|
appDomainIds.set(appName, existingFrontendDomain.domainId);
|
|
6032
6034
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6033
|
-
logger$
|
|
6035
|
+
logger$3.log(` ✓ Domain: https://${frontendHost} (existing)`);
|
|
6034
6036
|
} else try {
|
|
6035
6037
|
const domain = await api.createDomain({
|
|
6036
6038
|
host: frontendHost,
|
|
@@ -6042,10 +6044,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6042
6044
|
appHostnames.set(appName, frontendHost);
|
|
6043
6045
|
appDomainIds.set(appName, domain.domainId);
|
|
6044
6046
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6045
|
-
logger$
|
|
6047
|
+
logger$3.log(` ✓ Domain: https://${frontendHost} (created)`);
|
|
6046
6048
|
} catch (domainError) {
|
|
6047
6049
|
const message = domainError instanceof Error ? domainError.message : "Unknown error";
|
|
6048
|
-
logger$
|
|
6050
|
+
logger$3.log(` ⚠ Domain creation failed: ${message}`);
|
|
6049
6051
|
appHostnames.set(appName, frontendHost);
|
|
6050
6052
|
publicUrls[appName] = `https://${frontendHost}`;
|
|
6051
6053
|
}
|
|
@@ -6056,10 +6058,10 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6056
6058
|
applicationId: application.applicationId,
|
|
6057
6059
|
imageRef
|
|
6058
6060
|
});
|
|
6059
|
-
logger$
|
|
6061
|
+
logger$3.log(` ✓ ${appName} deployed successfully`);
|
|
6060
6062
|
} catch (error) {
|
|
6061
6063
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6062
|
-
logger$
|
|
6064
|
+
logger$3.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
6063
6065
|
results.push({
|
|
6064
6066
|
appName,
|
|
6065
6067
|
type: app.type,
|
|
@@ -6069,9 +6071,9 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6069
6071
|
}
|
|
6070
6072
|
}
|
|
6071
6073
|
}
|
|
6072
|
-
logger$
|
|
6074
|
+
logger$3.log("\n📋 Saving deploy state...");
|
|
6073
6075
|
await stateProvider.write(stage, state);
|
|
6074
|
-
logger$
|
|
6076
|
+
logger$3.log(" ✓ State saved");
|
|
6075
6077
|
const dnsConfig = workspace.deploy.dns;
|
|
6076
6078
|
if (dnsConfig && appHostnames.size > 0) {
|
|
6077
6079
|
const dnsResult = await orchestrateDns(appHostnames, dnsConfig, creds.endpoint);
|
|
@@ -6080,27 +6082,27 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
6080
6082
|
await stateProvider.write(stage, state);
|
|
6081
6083
|
}
|
|
6082
6084
|
if (dnsResult?.success && appHostnames.size > 0) {
|
|
6083
|
-
logger$
|
|
6085
|
+
logger$3.log("\n🔒 Validating domains for SSL certificates...");
|
|
6084
6086
|
for (const [appName, hostname] of appHostnames) try {
|
|
6085
6087
|
const result = await api.validateDomain(hostname);
|
|
6086
|
-
if (result.isValid) logger$
|
|
6087
|
-
else logger$
|
|
6088
|
+
if (result.isValid) logger$3.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);
|
|
6089
|
+
else logger$3.log(` ⚠ ${appName}: ${hostname} not valid`);
|
|
6088
6090
|
} catch (validationError) {
|
|
6089
6091
|
const message = validationError instanceof Error ? validationError.message : "Unknown error";
|
|
6090
|
-
logger$
|
|
6092
|
+
logger$3.log(` ⚠ ${appName}: validation failed - ${message}`);
|
|
6091
6093
|
}
|
|
6092
6094
|
}
|
|
6093
6095
|
}
|
|
6094
6096
|
const successCount = results.filter((r) => r.success).length;
|
|
6095
6097
|
const failedCount = results.filter((r) => !r.success).length;
|
|
6096
|
-
logger$
|
|
6097
|
-
logger$
|
|
6098
|
-
logger$
|
|
6099
|
-
logger$
|
|
6100
|
-
if (failedCount > 0) logger$
|
|
6098
|
+
logger$3.log(`\n${"─".repeat(50)}`);
|
|
6099
|
+
logger$3.log(`\n✅ Workspace deployment complete!`);
|
|
6100
|
+
logger$3.log(` Project: ${project.projectId}`);
|
|
6101
|
+
logger$3.log(` Successful: ${successCount}`);
|
|
6102
|
+
if (failedCount > 0) logger$3.log(` Failed: ${failedCount}`);
|
|
6101
6103
|
if (Object.keys(publicUrls).length > 0) {
|
|
6102
|
-
logger$
|
|
6103
|
-
for (const [name$1, url] of Object.entries(publicUrls)) logger$
|
|
6104
|
+
logger$3.log("\n 📡 Deployed URLs:");
|
|
6105
|
+
for (const [name$1, url] of Object.entries(publicUrls)) logger$3.log(` ${name$1}: ${url}`);
|
|
6104
6106
|
}
|
|
6105
6107
|
return {
|
|
6106
6108
|
apps: results,
|
|
@@ -6116,14 +6118,14 @@ async function deployCommand(options) {
|
|
|
6116
6118
|
const { provider, stage, tag, skipPush, skipBuild } = options;
|
|
6117
6119
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
6118
6120
|
if (loadedConfig.type === "workspace") {
|
|
6119
|
-
logger$
|
|
6121
|
+
logger$3.log("📦 Detected workspace configuration");
|
|
6120
6122
|
return workspaceDeployCommand(loadedConfig.workspace, options);
|
|
6121
6123
|
}
|
|
6122
|
-
logger$
|
|
6123
|
-
logger$
|
|
6124
|
+
logger$3.log(`\n🚀 Deploying to ${provider}...`);
|
|
6125
|
+
logger$3.log(` Stage: ${stage}`);
|
|
6124
6126
|
const config = await require_config.loadConfig();
|
|
6125
6127
|
const imageTag = tag ?? generateTag(stage);
|
|
6126
|
-
logger$
|
|
6128
|
+
logger$3.log(` Tag: ${imageTag}`);
|
|
6127
6129
|
const dockerConfig = resolveDockerConfig(config);
|
|
6128
6130
|
const imageName = dockerConfig.imageName;
|
|
6129
6131
|
const registry = dockerConfig.registry;
|
|
@@ -6132,7 +6134,7 @@ async function deployCommand(options) {
|
|
|
6132
6134
|
let finalRegistry = registry;
|
|
6133
6135
|
if (provider === "dokploy") {
|
|
6134
6136
|
const composeServices = config.docker?.compose?.services;
|
|
6135
|
-
logger$
|
|
6137
|
+
logger$3.log(`\n🔍 Docker compose config: ${JSON.stringify(config.docker?.compose)}`);
|
|
6136
6138
|
const dockerServices = composeServices ? Array.isArray(composeServices) ? {
|
|
6137
6139
|
postgres: composeServices.includes("postgres"),
|
|
6138
6140
|
redis: composeServices.includes("redis"),
|
|
@@ -6149,7 +6151,7 @@ async function deployCommand(options) {
|
|
|
6149
6151
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
6150
6152
|
let secrets = await readStageSecrets$1(stage);
|
|
6151
6153
|
if (!secrets) {
|
|
6152
|
-
logger$
|
|
6154
|
+
logger$3.log(` Creating secrets file for stage "${stage}"...`);
|
|
6153
6155
|
secrets = initStageSecrets(stage);
|
|
6154
6156
|
}
|
|
6155
6157
|
let updated = false;
|
|
@@ -6164,12 +6166,12 @@ async function deployCommand(options) {
|
|
|
6164
6166
|
const urlKey = key;
|
|
6165
6167
|
if (!secrets.urls[urlKey]) {
|
|
6166
6168
|
secrets.urls[urlKey] = value;
|
|
6167
|
-
logger$
|
|
6169
|
+
logger$3.log(` Saved ${key} to secrets.urls`);
|
|
6168
6170
|
updated = true;
|
|
6169
6171
|
}
|
|
6170
6172
|
} else if (!secrets.custom[key]) {
|
|
6171
6173
|
secrets.custom[key] = value;
|
|
6172
|
-
logger$
|
|
6174
|
+
logger$3.log(` Saved ${key} to secrets.custom`);
|
|
6173
6175
|
updated = true;
|
|
6174
6176
|
}
|
|
6175
6177
|
}
|
|
@@ -6178,14 +6180,14 @@ async function deployCommand(options) {
|
|
|
6178
6180
|
}
|
|
6179
6181
|
let masterKey;
|
|
6180
6182
|
if (!skipBuild) {
|
|
6181
|
-
logger$
|
|
6183
|
+
logger$3.log(`\n📦 Building for production...`);
|
|
6182
6184
|
const buildResult = await buildCommand({
|
|
6183
6185
|
provider: "server",
|
|
6184
6186
|
production: true,
|
|
6185
6187
|
stage
|
|
6186
6188
|
});
|
|
6187
6189
|
masterKey = buildResult.masterKey;
|
|
6188
|
-
} else logger$
|
|
6190
|
+
} else logger$3.log(`\n⏭️ Skipping build (--skip-build)`);
|
|
6189
6191
|
let result;
|
|
6190
6192
|
switch (provider) {
|
|
6191
6193
|
case "docker": {
|
|
@@ -6221,8 +6223,8 @@ async function deployCommand(options) {
|
|
|
6221
6223
|
break;
|
|
6222
6224
|
}
|
|
6223
6225
|
case "aws-lambda": {
|
|
6224
|
-
logger$
|
|
6225
|
-
logger$
|
|
6226
|
+
logger$3.log("\n⚠️ AWS Lambda deployment is not yet implemented.");
|
|
6227
|
+
logger$3.log(" Use SST or AWS CDK for Lambda deployments.");
|
|
6226
6228
|
result = {
|
|
6227
6229
|
imageRef,
|
|
6228
6230
|
masterKey
|
|
@@ -6231,7 +6233,7 @@ async function deployCommand(options) {
|
|
|
6231
6233
|
}
|
|
6232
6234
|
default: throw new Error(`Unknown deploy provider: ${provider}\nSupported providers: docker, dokploy, aws-lambda`);
|
|
6233
6235
|
}
|
|
6234
|
-
logger$
|
|
6236
|
+
logger$3.log("\n✅ Deployment complete!");
|
|
6235
6237
|
return result;
|
|
6236
6238
|
}
|
|
6237
6239
|
|
|
@@ -6520,6 +6522,83 @@ function rotateServicePassword(secrets, service) {
|
|
|
6520
6522
|
};
|
|
6521
6523
|
}
|
|
6522
6524
|
|
|
6525
|
+
//#endregion
|
|
6526
|
+
//#region src/setup/fullstack-secrets.ts
|
|
6527
|
+
/**
|
|
6528
|
+
* Generate a secure random password for database users.
|
|
6529
|
+
* Uses a combination of timestamp and random bytes for uniqueness.
|
|
6530
|
+
*/
|
|
6531
|
+
function generateDbPassword() {
|
|
6532
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
|
|
6533
|
+
}
|
|
6534
|
+
/**
|
|
6535
|
+
* Generate database URL for an app.
|
|
6536
|
+
* All apps connect to the same database, but use different users/schemas.
|
|
6537
|
+
*/
|
|
6538
|
+
function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
|
|
6539
|
+
const userName = appName.replace(/-/g, "_");
|
|
6540
|
+
const dbName = `${projectName.replace(/-/g, "_")}_dev`;
|
|
6541
|
+
return `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;
|
|
6542
|
+
}
|
|
6543
|
+
/**
|
|
6544
|
+
* Generate fullstack-aware custom secrets for a workspace.
|
|
6545
|
+
*
|
|
6546
|
+
* Generates:
|
|
6547
|
+
* - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
|
|
6548
|
+
* - Per-app database passwords and URLs for backend apps with db service
|
|
6549
|
+
* - Better-auth secrets for apps using the better-auth framework
|
|
6550
|
+
*/
|
|
6551
|
+
function generateFullstackCustomSecrets(workspace) {
|
|
6552
|
+
const hasDb = !!workspace.services.db;
|
|
6553
|
+
const customs = {
|
|
6554
|
+
NODE_ENV: "development",
|
|
6555
|
+
PORT: "3000",
|
|
6556
|
+
LOG_LEVEL: "debug",
|
|
6557
|
+
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
6558
|
+
};
|
|
6559
|
+
if (!hasDb) return customs;
|
|
6560
|
+
const frontendPorts = [];
|
|
6561
|
+
for (const [appName, appConfig] of Object.entries(workspace.apps)) {
|
|
6562
|
+
if (appConfig.type === "frontend") {
|
|
6563
|
+
frontendPorts.push(appConfig.port);
|
|
6564
|
+
continue;
|
|
6565
|
+
}
|
|
6566
|
+
const password = generateDbPassword();
|
|
6567
|
+
const upperName = appName.toUpperCase();
|
|
6568
|
+
customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
|
|
6569
|
+
customs[`${upperName}_DB_PASSWORD`] = password;
|
|
6570
|
+
if (appConfig.framework === "better-auth") {
|
|
6571
|
+
customs.AUTH_PORT = String(appConfig.port);
|
|
6572
|
+
customs.AUTH_URL = `http://localhost:${appConfig.port}`;
|
|
6573
|
+
customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
|
|
6574
|
+
customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
|
|
6575
|
+
}
|
|
6576
|
+
}
|
|
6577
|
+
if (customs.BETTER_AUTH_SECRET) {
|
|
6578
|
+
const allPorts = Object.values(workspace.apps).map((a) => a.port);
|
|
6579
|
+
customs.BETTER_AUTH_TRUSTED_ORIGINS = allPorts.map((p) => `http://localhost:${p}`).join(",");
|
|
6580
|
+
}
|
|
6581
|
+
return customs;
|
|
6582
|
+
}
|
|
6583
|
+
/**
|
|
6584
|
+
* Extract *_DB_PASSWORD keys from secrets and write docker/.env.
|
|
6585
|
+
*
|
|
6586
|
+
* The docker/.env file contains database passwords that the PostgreSQL
|
|
6587
|
+
* init script reads to create per-app database users.
|
|
6588
|
+
*/
|
|
6589
|
+
async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
|
|
6590
|
+
const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
|
|
6591
|
+
if (dbPasswordEntries.length === 0) return;
|
|
6592
|
+
const envContent = `# Auto-generated docker environment file
|
|
6593
|
+
# Contains database passwords for docker-compose postgres init
|
|
6594
|
+
# This file is gitignored - do not commit to version control
|
|
6595
|
+
${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
|
|
6596
|
+
`;
|
|
6597
|
+
const envPath = (0, node_path.join)(workspaceRoot, "docker", ".env");
|
|
6598
|
+
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(envPath), { recursive: true });
|
|
6599
|
+
await (0, node_fs_promises.writeFile)(envPath, envContent);
|
|
6600
|
+
}
|
|
6601
|
+
|
|
6523
6602
|
//#endregion
|
|
6524
6603
|
//#region src/init/versions.ts
|
|
6525
6604
|
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
@@ -6547,7 +6626,7 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6547
6626
|
"@geekmidas/cache": "~1.0.0",
|
|
6548
6627
|
"@geekmidas/client": "~2.0.0",
|
|
6549
6628
|
"@geekmidas/cloud": "~1.0.0",
|
|
6550
|
-
"@geekmidas/constructs": "~1.1.
|
|
6629
|
+
"@geekmidas/constructs": "~1.1.1",
|
|
6551
6630
|
"@geekmidas/db": "~1.0.0",
|
|
6552
6631
|
"@geekmidas/emailkit": "~1.0.0",
|
|
6553
6632
|
"@geekmidas/envkit": "~1.0.3",
|
|
@@ -10664,25 +10743,77 @@ function getRunCommand(pkgManager, script) {
|
|
|
10664
10743
|
default: return `npm run ${script}`;
|
|
10665
10744
|
}
|
|
10666
10745
|
}
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10746
|
+
const lockfileByPm = {
|
|
10747
|
+
pnpm: "pnpm-lock.yaml",
|
|
10748
|
+
yarn: "yarn.lock",
|
|
10749
|
+
npm: "package-lock.json",
|
|
10750
|
+
bun: "bun.lockb"
|
|
10751
|
+
};
|
|
10670
10752
|
/**
|
|
10671
|
-
*
|
|
10753
|
+
* Find the workspace/project root by walking up from cwd.
|
|
10754
|
+
* Checks for PM-specific workspace config, package.json#workspaces,
|
|
10755
|
+
* and lockfiles.
|
|
10672
10756
|
*/
|
|
10673
|
-
function
|
|
10674
|
-
|
|
10757
|
+
function findWorkspaceRoot(cwd, pm) {
|
|
10758
|
+
let dir = cwd;
|
|
10759
|
+
const root = (0, node_path.parse)(dir).root;
|
|
10760
|
+
const lockfile = lockfileByPm[pm];
|
|
10761
|
+
while (dir !== root) {
|
|
10762
|
+
if (pm === "pnpm" && (0, node_fs.existsSync)((0, node_path.join)(dir, "pnpm-workspace.yaml"))) return dir;
|
|
10763
|
+
const pkgJsonPath = (0, node_path.join)(dir, "package.json");
|
|
10764
|
+
if ((0, node_fs.existsSync)(pkgJsonPath)) try {
|
|
10765
|
+
const pkg$1 = JSON.parse((0, node_fs.readFileSync)(pkgJsonPath, "utf-8"));
|
|
10766
|
+
if (pkg$1.workspaces) return dir;
|
|
10767
|
+
} catch {}
|
|
10768
|
+
if ((0, node_fs.existsSync)((0, node_path.join)(dir, lockfile))) return dir;
|
|
10769
|
+
dir = (0, node_path.dirname)(dir);
|
|
10770
|
+
}
|
|
10771
|
+
return cwd;
|
|
10675
10772
|
}
|
|
10676
10773
|
/**
|
|
10677
|
-
*
|
|
10678
|
-
*
|
|
10774
|
+
* Get workspace package glob patterns from pnpm-workspace.yaml
|
|
10775
|
+
* or package.json#workspaces.
|
|
10679
10776
|
*/
|
|
10680
|
-
function
|
|
10681
|
-
const
|
|
10682
|
-
|
|
10683
|
-
|
|
10777
|
+
function getWorkspaceGlobs(root) {
|
|
10778
|
+
const pnpmWorkspacePath = (0, node_path.join)(root, "pnpm-workspace.yaml");
|
|
10779
|
+
if ((0, node_fs.existsSync)(pnpmWorkspacePath)) {
|
|
10780
|
+
const content = (0, node_fs.readFileSync)(pnpmWorkspacePath, "utf-8");
|
|
10781
|
+
const parsed = (0, yaml.parse)(content);
|
|
10782
|
+
return parsed?.packages ?? [];
|
|
10783
|
+
}
|
|
10784
|
+
const rootPkgJsonPath = (0, node_path.join)(root, "package.json");
|
|
10785
|
+
if ((0, node_fs.existsSync)(rootPkgJsonPath)) {
|
|
10786
|
+
const pkg$1 = JSON.parse((0, node_fs.readFileSync)(rootPkgJsonPath, "utf-8"));
|
|
10787
|
+
if (Array.isArray(pkg$1.workspaces)) return pkg$1.workspaces;
|
|
10788
|
+
if (pkg$1.workspaces?.packages) return pkg$1.workspaces.packages;
|
|
10789
|
+
}
|
|
10790
|
+
return [];
|
|
10684
10791
|
}
|
|
10685
10792
|
/**
|
|
10793
|
+
* Find all package.json files across a workspace.
|
|
10794
|
+
* Returns the root package.json plus all workspace member package.json paths.
|
|
10795
|
+
*/
|
|
10796
|
+
function findWorkspacePackages(cwd, pm) {
|
|
10797
|
+
const workspaceRoot = findWorkspaceRoot(cwd, pm);
|
|
10798
|
+
const results = [];
|
|
10799
|
+
const rootPkgJson = (0, node_path.join)(workspaceRoot, "package.json");
|
|
10800
|
+
if ((0, node_fs.existsSync)(rootPkgJson)) results.push(rootPkgJson);
|
|
10801
|
+
const globs = getWorkspaceGlobs(workspaceRoot);
|
|
10802
|
+
for (const glob of globs) {
|
|
10803
|
+
const pattern = `${glob}/package.json`;
|
|
10804
|
+
const matches = fast_glob.default.sync(pattern, {
|
|
10805
|
+
cwd: workspaceRoot,
|
|
10806
|
+
absolute: true,
|
|
10807
|
+
ignore: ["**/node_modules/**"]
|
|
10808
|
+
});
|
|
10809
|
+
results.push(...matches);
|
|
10810
|
+
}
|
|
10811
|
+
return [...new Set(results)];
|
|
10812
|
+
}
|
|
10813
|
+
|
|
10814
|
+
//#endregion
|
|
10815
|
+
//#region src/init/index.ts
|
|
10816
|
+
/**
|
|
10686
10817
|
* Main init command - scaffolds a new project
|
|
10687
10818
|
*/
|
|
10688
10819
|
async function initCommand(projectName, options = {}) {
|
|
@@ -10984,7 +11115,7 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
10984
11115
|
|
|
10985
11116
|
//#endregion
|
|
10986
11117
|
//#region src/secrets/index.ts
|
|
10987
|
-
const logger = console;
|
|
11118
|
+
const logger$2 = console;
|
|
10988
11119
|
/**
|
|
10989
11120
|
* Extract service names from compose config.
|
|
10990
11121
|
*/
|
|
@@ -11000,23 +11131,33 @@ function getServicesFromConfig(services) {
|
|
|
11000
11131
|
async function secretsInitCommand(options) {
|
|
11001
11132
|
const { stage, force } = options;
|
|
11002
11133
|
if (!force && require_storage.secretsExist(stage)) {
|
|
11003
|
-
logger.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
|
|
11134
|
+
logger$2.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
|
|
11004
11135
|
process.exit(1);
|
|
11005
11136
|
}
|
|
11006
11137
|
const config = await require_config.loadConfig();
|
|
11007
11138
|
const services = getServicesFromConfig(config.docker?.compose?.services);
|
|
11008
|
-
if (services.length === 0) logger.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
|
|
11139
|
+
if (services.length === 0) logger$2.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
|
|
11009
11140
|
const secrets = createStageSecrets(stage, services);
|
|
11141
|
+
try {
|
|
11142
|
+
const loaded = await require_config.loadWorkspaceConfig();
|
|
11143
|
+
const isMultiApp = Object.keys(loaded.workspace.apps).length > 1;
|
|
11144
|
+
if (isMultiApp) {
|
|
11145
|
+
const customSecrets = generateFullstackCustomSecrets(loaded.workspace);
|
|
11146
|
+
secrets.custom = customSecrets;
|
|
11147
|
+
logger$2.log(" Detected workspace mode — generating per-app secrets");
|
|
11148
|
+
}
|
|
11149
|
+
} catch {}
|
|
11010
11150
|
await require_storage.writeStageSecrets(secrets);
|
|
11011
|
-
logger.log(`\n✓ Secrets initialized for stage "${stage}"`);
|
|
11012
|
-
logger.log(` Location: .gkm/secrets/${stage}.json`);
|
|
11013
|
-
logger.log("\n Generated credentials for:");
|
|
11014
|
-
for (const service of services) logger.log(` - ${service}`);
|
|
11015
|
-
if (secrets.urls.DATABASE_URL) logger.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11016
|
-
if (secrets.urls.REDIS_URL) logger.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11017
|
-
if (secrets.urls.RABBITMQ_URL) logger.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11018
|
-
logger.log(`\n
|
|
11019
|
-
logger.log(
|
|
11151
|
+
logger$2.log(`\n✓ Secrets initialized for stage "${stage}"`);
|
|
11152
|
+
logger$2.log(` Location: .gkm/secrets/${stage}.json`);
|
|
11153
|
+
logger$2.log("\n Generated credentials for:");
|
|
11154
|
+
for (const service of services) logger$2.log(` - ${service}`);
|
|
11155
|
+
if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11156
|
+
if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11157
|
+
if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11158
|
+
if (Object.keys(secrets.custom).length > 0) logger$2.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
|
|
11159
|
+
logger$2.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
|
|
11160
|
+
logger$2.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
|
|
11020
11161
|
}
|
|
11021
11162
|
/**
|
|
11022
11163
|
* Read all data from stdin.
|
|
@@ -11035,21 +11176,21 @@ async function secretsSetCommand(key, value, options) {
|
|
|
11035
11176
|
let secretValue = value;
|
|
11036
11177
|
if (!secretValue) {
|
|
11037
11178
|
if (process.stdin.isTTY) {
|
|
11038
|
-
logger.error("No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>");
|
|
11039
|
-
logger.error("Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>");
|
|
11179
|
+
logger$2.error("No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>");
|
|
11180
|
+
logger$2.error("Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>");
|
|
11040
11181
|
process.exit(1);
|
|
11041
11182
|
}
|
|
11042
11183
|
secretValue = await readStdin();
|
|
11043
11184
|
if (!secretValue) {
|
|
11044
|
-
logger.error("No value received from stdin");
|
|
11185
|
+
logger$2.error("No value received from stdin");
|
|
11045
11186
|
process.exit(1);
|
|
11046
11187
|
}
|
|
11047
11188
|
}
|
|
11048
11189
|
try {
|
|
11049
11190
|
await require_storage.setCustomSecret(stage, key, secretValue);
|
|
11050
|
-
logger.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
|
|
11191
|
+
logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
|
|
11051
11192
|
} catch (error) {
|
|
11052
|
-
logger.error(error instanceof Error ? error.message : "Failed to set secret");
|
|
11193
|
+
logger$2.error(error instanceof Error ? error.message : "Failed to set secret");
|
|
11053
11194
|
process.exit(1);
|
|
11054
11195
|
}
|
|
11055
11196
|
}
|
|
@@ -11060,32 +11201,32 @@ async function secretsShowCommand(options) {
|
|
|
11060
11201
|
const { stage, reveal } = options;
|
|
11061
11202
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
11062
11203
|
if (!secrets) {
|
|
11063
|
-
logger.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11204
|
+
logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11064
11205
|
process.exit(1);
|
|
11065
11206
|
}
|
|
11066
|
-
logger.log(`\nSecrets for stage "${stage}":`);
|
|
11067
|
-
logger.log(` Created: ${secrets.createdAt}`);
|
|
11068
|
-
logger.log(` Updated: ${secrets.updatedAt}`);
|
|
11069
|
-
logger.log("\nService Credentials:");
|
|
11207
|
+
logger$2.log(`\nSecrets for stage "${stage}":`);
|
|
11208
|
+
logger$2.log(` Created: ${secrets.createdAt}`);
|
|
11209
|
+
logger$2.log(` Updated: ${secrets.updatedAt}`);
|
|
11210
|
+
logger$2.log("\nService Credentials:");
|
|
11070
11211
|
for (const [service, creds] of Object.entries(secrets.services)) if (creds) {
|
|
11071
|
-
logger.log(`\n ${service}:`);
|
|
11072
|
-
logger.log(` host: ${creds.host}`);
|
|
11073
|
-
logger.log(` port: ${creds.port}`);
|
|
11074
|
-
logger.log(` username: ${creds.username}`);
|
|
11075
|
-
logger.log(` password: ${reveal ? creds.password : require_storage.maskPassword(creds.password)}`);
|
|
11076
|
-
if (creds.database) logger.log(` database: ${creds.database}`);
|
|
11077
|
-
if (creds.vhost) logger.log(` vhost: ${creds.vhost}`);
|
|
11078
|
-
}
|
|
11079
|
-
logger.log("\nConnection URLs:");
|
|
11080
|
-
if (secrets.urls.DATABASE_URL) logger.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11081
|
-
if (secrets.urls.REDIS_URL) logger.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11082
|
-
if (secrets.urls.RABBITMQ_URL) logger.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11212
|
+
logger$2.log(`\n ${service}:`);
|
|
11213
|
+
logger$2.log(` host: ${creds.host}`);
|
|
11214
|
+
logger$2.log(` port: ${creds.port}`);
|
|
11215
|
+
logger$2.log(` username: ${creds.username}`);
|
|
11216
|
+
logger$2.log(` password: ${reveal ? creds.password : require_storage.maskPassword(creds.password)}`);
|
|
11217
|
+
if (creds.database) logger$2.log(` database: ${creds.database}`);
|
|
11218
|
+
if (creds.vhost) logger$2.log(` vhost: ${creds.vhost}`);
|
|
11219
|
+
}
|
|
11220
|
+
logger$2.log("\nConnection URLs:");
|
|
11221
|
+
if (secrets.urls.DATABASE_URL) logger$2.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11222
|
+
if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11223
|
+
if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11083
11224
|
const customKeys = Object.keys(secrets.custom);
|
|
11084
11225
|
if (customKeys.length > 0) {
|
|
11085
|
-
logger.log("\nCustom Secrets:");
|
|
11086
|
-
for (const [key, value] of Object.entries(secrets.custom)) logger.log(` ${key}: ${reveal ? value : require_storage.maskPassword(value)}`);
|
|
11226
|
+
logger$2.log("\nCustom Secrets:");
|
|
11227
|
+
for (const [key, value] of Object.entries(secrets.custom)) logger$2.log(` ${key}: ${reveal ? value : require_storage.maskPassword(value)}`);
|
|
11087
11228
|
}
|
|
11088
|
-
if (!reveal) logger.log("\nUse --reveal to show actual values");
|
|
11229
|
+
if (!reveal) logger$2.log("\nUse --reveal to show actual values");
|
|
11089
11230
|
}
|
|
11090
11231
|
/**
|
|
11091
11232
|
* Rotate passwords for services.
|
|
@@ -11094,25 +11235,25 @@ async function secretsRotateCommand(options) {
|
|
|
11094
11235
|
const { stage, service } = options;
|
|
11095
11236
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
11096
11237
|
if (!secrets) {
|
|
11097
|
-
logger.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11238
|
+
logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11098
11239
|
process.exit(1);
|
|
11099
11240
|
}
|
|
11100
11241
|
if (service) {
|
|
11101
11242
|
if (!secrets.services[service]) {
|
|
11102
|
-
logger.error(`Service "${service}" not configured in stage "${stage}"`);
|
|
11243
|
+
logger$2.error(`Service "${service}" not configured in stage "${stage}"`);
|
|
11103
11244
|
process.exit(1);
|
|
11104
11245
|
}
|
|
11105
11246
|
const updated = rotateServicePassword(secrets, service);
|
|
11106
11247
|
await require_storage.writeStageSecrets(updated);
|
|
11107
|
-
logger.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
|
|
11248
|
+
logger$2.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
|
|
11108
11249
|
} else {
|
|
11109
11250
|
let updated = secrets;
|
|
11110
11251
|
const services = Object.keys(secrets.services);
|
|
11111
11252
|
for (const svc of services) updated = rotateServicePassword(updated, svc);
|
|
11112
11253
|
await require_storage.writeStageSecrets(updated);
|
|
11113
|
-
logger.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
|
|
11254
|
+
logger$2.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
|
|
11114
11255
|
}
|
|
11115
|
-
logger.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
|
|
11256
|
+
logger$2.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
|
|
11116
11257
|
}
|
|
11117
11258
|
/**
|
|
11118
11259
|
* Import secrets from a JSON file.
|
|
@@ -11120,7 +11261,7 @@ async function secretsRotateCommand(options) {
|
|
|
11120
11261
|
async function secretsImportCommand(file, options) {
|
|
11121
11262
|
const { stage, merge = true } = options;
|
|
11122
11263
|
if (!(0, node_fs.existsSync)(file)) {
|
|
11123
|
-
logger.error(`File not found: ${file}`);
|
|
11264
|
+
logger$2.error(`File not found: ${file}`);
|
|
11124
11265
|
process.exit(1);
|
|
11125
11266
|
}
|
|
11126
11267
|
let importedSecrets;
|
|
@@ -11130,12 +11271,12 @@ async function secretsImportCommand(file, options) {
|
|
|
11130
11271
|
if (typeof importedSecrets !== "object" || importedSecrets === null) throw new Error("JSON must be an object");
|
|
11131
11272
|
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}`);
|
|
11132
11273
|
} catch (error) {
|
|
11133
|
-
logger.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
|
|
11274
|
+
logger$2.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
|
|
11134
11275
|
process.exit(1);
|
|
11135
11276
|
}
|
|
11136
11277
|
const secrets = await require_storage.readStageSecrets(stage);
|
|
11137
11278
|
if (!secrets) {
|
|
11138
|
-
logger.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11279
|
+
logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
11139
11280
|
process.exit(1);
|
|
11140
11281
|
}
|
|
11141
11282
|
const updatedCustom = merge ? {
|
|
@@ -11150,10 +11291,10 @@ async function secretsImportCommand(file, options) {
|
|
|
11150
11291
|
await require_storage.writeStageSecrets(updated);
|
|
11151
11292
|
const importedCount = Object.keys(importedSecrets).length;
|
|
11152
11293
|
const totalCount = Object.keys(updatedCustom).length;
|
|
11153
|
-
logger.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
|
|
11154
|
-
if (merge && totalCount > importedCount) logger.log(` Total custom secrets: ${totalCount}`);
|
|
11155
|
-
logger.log("\n Imported keys:");
|
|
11156
|
-
for (const key of Object.keys(importedSecrets)) logger.log(` - ${key}`);
|
|
11294
|
+
logger$2.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
|
|
11295
|
+
if (merge && totalCount > importedCount) logger$2.log(` Total custom secrets: ${totalCount}`);
|
|
11296
|
+
logger$2.log("\n Imported keys:");
|
|
11297
|
+
for (const key of Object.keys(importedSecrets)) logger$2.log(` - ${key}`);
|
|
11157
11298
|
}
|
|
11158
11299
|
/**
|
|
11159
11300
|
* Mask password in a URL for display.
|
|
@@ -11168,6 +11309,139 @@ function maskUrl(url) {
|
|
|
11168
11309
|
}
|
|
11169
11310
|
}
|
|
11170
11311
|
|
|
11312
|
+
//#endregion
|
|
11313
|
+
//#region src/setup/index.ts
|
|
11314
|
+
const logger$1 = console;
|
|
11315
|
+
/**
|
|
11316
|
+
* Setup development environment.
|
|
11317
|
+
*
|
|
11318
|
+
* Orchestrates:
|
|
11319
|
+
* 1. Load workspace config
|
|
11320
|
+
* 2. Resolve secrets (local → SSM → generate fresh)
|
|
11321
|
+
* 3. Write docker/.env from secrets
|
|
11322
|
+
* 4. Start Docker services
|
|
11323
|
+
*/
|
|
11324
|
+
async function setupCommand(options = {}) {
|
|
11325
|
+
const stage = options.stage ?? "development";
|
|
11326
|
+
logger$1.log("\n🔧 Setting up development environment...\n");
|
|
11327
|
+
let loadedConfig;
|
|
11328
|
+
try {
|
|
11329
|
+
loadedConfig = await require_config.loadWorkspaceConfig();
|
|
11330
|
+
} catch {
|
|
11331
|
+
logger$1.error("❌ No gkm.config.ts found. Run this command from a workspace root.");
|
|
11332
|
+
process.exit(1);
|
|
11333
|
+
}
|
|
11334
|
+
const { workspace } = loadedConfig;
|
|
11335
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
11336
|
+
logger$1.log(`📦 Workspace: ${workspace.name}`);
|
|
11337
|
+
logger$1.log(`📱 Apps: ${Object.keys(workspace.apps).join(", ")}`);
|
|
11338
|
+
logger$1.log(`🔑 Stage: ${stage}\n`);
|
|
11339
|
+
const secrets = await resolveSecrets(stage, workspace, options);
|
|
11340
|
+
if (!secrets) {
|
|
11341
|
+
logger$1.error("❌ Failed to resolve secrets. Exiting.");
|
|
11342
|
+
process.exit(1);
|
|
11343
|
+
}
|
|
11344
|
+
if (isMultiApp && workspace.services.db) {
|
|
11345
|
+
await writeDockerEnvFromSecrets(secrets, workspace.root);
|
|
11346
|
+
logger$1.log("📄 Generated docker/.env with database passwords");
|
|
11347
|
+
}
|
|
11348
|
+
if (!options.skipDocker) {
|
|
11349
|
+
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
11350
|
+
if ((0, node_fs.existsSync)(composeFile)) {
|
|
11351
|
+
logger$1.log("");
|
|
11352
|
+
await startWorkspaceServices(workspace);
|
|
11353
|
+
} else logger$1.log("⚠️ No docker-compose.yml found. Skipping Docker services.");
|
|
11354
|
+
}
|
|
11355
|
+
printSummary(workspace, stage);
|
|
11356
|
+
}
|
|
11357
|
+
/**
|
|
11358
|
+
* Resolve secrets with priority:
|
|
11359
|
+
* 1. Local secrets exist → use them (preserves manual additions)
|
|
11360
|
+
* 2. SSM configured and has secrets → pull and use
|
|
11361
|
+
* 3. Neither → generate fresh secrets
|
|
11362
|
+
*
|
|
11363
|
+
* --force skips checks 1 and 2 and always regenerates.
|
|
11364
|
+
*/
|
|
11365
|
+
async function resolveSecrets(stage, workspace, options) {
|
|
11366
|
+
if (options.force) {
|
|
11367
|
+
logger$1.log("🔐 Generating fresh secrets (--force)...");
|
|
11368
|
+
return generateFreshSecrets(stage, workspace, options);
|
|
11369
|
+
}
|
|
11370
|
+
if (require_storage.secretsExist(stage, workspace.root)) {
|
|
11371
|
+
logger$1.log("🔐 Using existing local secrets");
|
|
11372
|
+
const secrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
11373
|
+
if (secrets) return secrets;
|
|
11374
|
+
}
|
|
11375
|
+
if (require_sync.isSSMConfigured(workspace)) {
|
|
11376
|
+
logger$1.log("☁️ Checking for remote secrets in SSM...");
|
|
11377
|
+
try {
|
|
11378
|
+
const remoteSecrets = await require_sync.pullSecrets(stage, workspace);
|
|
11379
|
+
if (remoteSecrets) {
|
|
11380
|
+
logger$1.log("✅ Pulled secrets from SSM");
|
|
11381
|
+
await require_storage.writeStageSecrets(remoteSecrets, workspace.root);
|
|
11382
|
+
return remoteSecrets;
|
|
11383
|
+
}
|
|
11384
|
+
logger$1.log(" No remote secrets found");
|
|
11385
|
+
} catch (error) {
|
|
11386
|
+
logger$1.warn(`⚠️ Failed to pull from SSM: ${error.message}`);
|
|
11387
|
+
}
|
|
11388
|
+
}
|
|
11389
|
+
logger$1.log("🔐 Generating fresh development secrets...");
|
|
11390
|
+
return generateFreshSecrets(stage, workspace, options);
|
|
11391
|
+
}
|
|
11392
|
+
/**
|
|
11393
|
+
* Generate fresh secrets for the workspace.
|
|
11394
|
+
*/
|
|
11395
|
+
async function generateFreshSecrets(stage, workspace, options) {
|
|
11396
|
+
const serviceNames = [];
|
|
11397
|
+
if (workspace.services.db) serviceNames.push("postgres");
|
|
11398
|
+
if (workspace.services.cache) serviceNames.push("redis");
|
|
11399
|
+
const secrets = createStageSecrets(stage, serviceNames);
|
|
11400
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
11401
|
+
if (isMultiApp) {
|
|
11402
|
+
const customSecrets = generateFullstackCustomSecrets(workspace);
|
|
11403
|
+
secrets.custom = customSecrets;
|
|
11404
|
+
} else secrets.custom = {
|
|
11405
|
+
NODE_ENV: "development",
|
|
11406
|
+
PORT: "3000",
|
|
11407
|
+
LOG_LEVEL: "debug",
|
|
11408
|
+
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
11409
|
+
};
|
|
11410
|
+
await require_storage.writeStageSecrets(secrets, workspace.root);
|
|
11411
|
+
logger$1.log(` Secrets written to .gkm/secrets/${stage}.json`);
|
|
11412
|
+
if (require_sync.isSSMConfigured(workspace) && !options.yes) {
|
|
11413
|
+
const { shouldPush } = await (0, prompts.default)({
|
|
11414
|
+
type: "confirm",
|
|
11415
|
+
name: "shouldPush",
|
|
11416
|
+
message: "Push secrets to SSM for team sharing?",
|
|
11417
|
+
initial: true
|
|
11418
|
+
});
|
|
11419
|
+
if (shouldPush) try {
|
|
11420
|
+
await require_sync.pushSecrets(stage, workspace);
|
|
11421
|
+
logger$1.log("☁️ Secrets pushed to SSM");
|
|
11422
|
+
} catch (error) {
|
|
11423
|
+
logger$1.warn(`⚠️ Failed to push to SSM: ${error.message}`);
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
return secrets;
|
|
11427
|
+
}
|
|
11428
|
+
/**
|
|
11429
|
+
* Print setup summary with next steps.
|
|
11430
|
+
*/
|
|
11431
|
+
function printSummary(workspace, stage) {
|
|
11432
|
+
logger$1.log(`\n${"─".repeat(50)}`);
|
|
11433
|
+
logger$1.log("\n✅ Development environment ready!\n");
|
|
11434
|
+
logger$1.log("📋 Apps:");
|
|
11435
|
+
for (const [name$1, app] of Object.entries(workspace.apps)) {
|
|
11436
|
+
const icon = app.type === "frontend" ? "🌐" : "🔧";
|
|
11437
|
+
logger$1.log(` ${icon} ${name$1} → http://localhost:${app.port}`);
|
|
11438
|
+
}
|
|
11439
|
+
logger$1.log("\n🚀 Next steps:");
|
|
11440
|
+
logger$1.log(" gkm dev # Start all apps");
|
|
11441
|
+
logger$1.log(` gkm secrets:show --stage ${stage} # View secrets`);
|
|
11442
|
+
logger$1.log("");
|
|
11443
|
+
}
|
|
11444
|
+
|
|
11171
11445
|
//#endregion
|
|
11172
11446
|
//#region src/test/index.ts
|
|
11173
11447
|
/**
|
|
@@ -11304,6 +11578,146 @@ async function ensureTestDatabase(env) {
|
|
|
11304
11578
|
}
|
|
11305
11579
|
}
|
|
11306
11580
|
|
|
11581
|
+
//#endregion
|
|
11582
|
+
//#region src/upgrade/index.ts
|
|
11583
|
+
const logger = console;
|
|
11584
|
+
async function upgradeCommand(options = {}) {
|
|
11585
|
+
const cwd = process.cwd();
|
|
11586
|
+
logger.log("\n📦 Scanning workspace for @geekmidas packages...\n");
|
|
11587
|
+
const pm = detectPackageManager(cwd);
|
|
11588
|
+
logger.log(` Package manager: ${pm}`);
|
|
11589
|
+
const packageJsonPaths = findWorkspacePackages(cwd, pm);
|
|
11590
|
+
logger.log(` Found ${packageJsonPaths.length} package(s) in workspace\n`);
|
|
11591
|
+
const dependencies$1 = scanForGeekmidasDeps(packageJsonPaths);
|
|
11592
|
+
if (dependencies$1.length === 0) {
|
|
11593
|
+
logger.log(" No @geekmidas packages found.\n");
|
|
11594
|
+
return;
|
|
11595
|
+
}
|
|
11596
|
+
const uniquePackages = [...new Set(dependencies$1.map((d) => d.packageName))];
|
|
11597
|
+
logger.log(` Checking ${uniquePackages.length} unique @geekmidas package(s) on npm...\n`);
|
|
11598
|
+
const latestVersions = await fetchLatestVersions(uniquePackages);
|
|
11599
|
+
const upgradeInfos = dependencies$1.map((dep) => resolveUpgradeInfo(dep, latestVersions));
|
|
11600
|
+
printUpgradeTable(upgradeInfos);
|
|
11601
|
+
const upgradable = upgradeInfos.filter((info) => info.needsUpgrade && !info.isWorkspaceRef);
|
|
11602
|
+
if (upgradable.length === 0) {
|
|
11603
|
+
logger.log("\n All @geekmidas packages are up to date!\n");
|
|
11604
|
+
return;
|
|
11605
|
+
}
|
|
11606
|
+
logger.log(`\n ${upgradable.length} package(s) can be upgraded.\n`);
|
|
11607
|
+
if (options.dryRun) {
|
|
11608
|
+
logger.log(" --dry-run: No changes made.\n");
|
|
11609
|
+
printUpgradeCommands(upgradable, pm);
|
|
11610
|
+
return;
|
|
11611
|
+
}
|
|
11612
|
+
executeUpgrade(upgradable, pm, cwd);
|
|
11613
|
+
logger.log("\n ✅ Upgrade complete! Run your tests to verify.\n");
|
|
11614
|
+
}
|
|
11615
|
+
function scanForGeekmidasDeps(packageJsonPaths) {
|
|
11616
|
+
const results = [];
|
|
11617
|
+
const depTypes = [
|
|
11618
|
+
"dependencies",
|
|
11619
|
+
"devDependencies",
|
|
11620
|
+
"peerDependencies"
|
|
11621
|
+
];
|
|
11622
|
+
for (const pkgJsonPath of packageJsonPaths) {
|
|
11623
|
+
const pkg$1 = JSON.parse((0, node_fs.readFileSync)(pkgJsonPath, "utf-8"));
|
|
11624
|
+
const workspaceName = pkg$1.name ?? pkgJsonPath;
|
|
11625
|
+
for (const depType of depTypes) {
|
|
11626
|
+
const deps = pkg$1[depType];
|
|
11627
|
+
if (!deps) continue;
|
|
11628
|
+
for (const [name$1, version$1] of Object.entries(deps)) {
|
|
11629
|
+
if (!name$1.startsWith("@geekmidas/")) continue;
|
|
11630
|
+
results.push({
|
|
11631
|
+
packageName: name$1,
|
|
11632
|
+
currentVersion: version$1,
|
|
11633
|
+
depType,
|
|
11634
|
+
packageJsonPath: pkgJsonPath,
|
|
11635
|
+
workspaceName
|
|
11636
|
+
});
|
|
11637
|
+
}
|
|
11638
|
+
}
|
|
11639
|
+
}
|
|
11640
|
+
return results;
|
|
11641
|
+
}
|
|
11642
|
+
async function fetchLatestVersions(packageNames) {
|
|
11643
|
+
const versions = /* @__PURE__ */ new Map();
|
|
11644
|
+
const results = await Promise.allSettled(packageNames.map(async (name$1) => {
|
|
11645
|
+
const res = await fetch(`https://registry.npmjs.org/${name$1}/latest`);
|
|
11646
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
11647
|
+
const data = await res.json();
|
|
11648
|
+
return {
|
|
11649
|
+
name: name$1,
|
|
11650
|
+
version: data.version
|
|
11651
|
+
};
|
|
11652
|
+
}));
|
|
11653
|
+
for (const result of results) if (result.status === "fulfilled") versions.set(result.value.name, result.value.version);
|
|
11654
|
+
return versions;
|
|
11655
|
+
}
|
|
11656
|
+
function resolveUpgradeInfo(dep, latestVersions) {
|
|
11657
|
+
const isWorkspaceRef = dep.currentVersion.startsWith("workspace:");
|
|
11658
|
+
const latestVersion = latestVersions.get(dep.packageName) ?? "unknown";
|
|
11659
|
+
const currentBare = dep.currentVersion.replace(/^[\^~>=<]*/g, "");
|
|
11660
|
+
const needsUpgrade = !isWorkspaceRef && latestVersion !== "unknown" && currentBare !== latestVersion;
|
|
11661
|
+
return {
|
|
11662
|
+
...dep,
|
|
11663
|
+
latestVersion,
|
|
11664
|
+
isWorkspaceRef,
|
|
11665
|
+
needsUpgrade
|
|
11666
|
+
};
|
|
11667
|
+
}
|
|
11668
|
+
function printUpgradeTable(infos) {
|
|
11669
|
+
const byPackage = /* @__PURE__ */ new Map();
|
|
11670
|
+
for (const info of infos) if (!byPackage.has(info.packageName)) byPackage.set(info.packageName, info);
|
|
11671
|
+
const nameWidth = 33;
|
|
11672
|
+
const verWidth = 14;
|
|
11673
|
+
const statusWidth = 14;
|
|
11674
|
+
const hr = ` ${"─".repeat(nameWidth + verWidth * 2 + statusWidth + 5)}`;
|
|
11675
|
+
logger.log(hr);
|
|
11676
|
+
logger.log(` ${"Package".padEnd(nameWidth)} ${"Current".padEnd(verWidth)} ${"Latest".padEnd(verWidth)} ${"Status".padEnd(statusWidth)}`);
|
|
11677
|
+
logger.log(hr);
|
|
11678
|
+
for (const [, info] of byPackage) {
|
|
11679
|
+
const name$1 = info.packageName.padEnd(nameWidth);
|
|
11680
|
+
const current = info.currentVersion.padEnd(verWidth);
|
|
11681
|
+
const latest = info.latestVersion.padEnd(verWidth);
|
|
11682
|
+
let status;
|
|
11683
|
+
if (info.isWorkspaceRef) status = "workspace";
|
|
11684
|
+
else if (info.needsUpgrade) status = "⬆ upgrade";
|
|
11685
|
+
else status = "✓ up-to-date";
|
|
11686
|
+
logger.log(` ${name$1} ${current} ${latest} ${status}`);
|
|
11687
|
+
}
|
|
11688
|
+
logger.log(hr);
|
|
11689
|
+
}
|
|
11690
|
+
function printUpgradeCommands(upgradable, pm) {
|
|
11691
|
+
logger.log(" Commands that would be run:\n");
|
|
11692
|
+
const uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];
|
|
11693
|
+
const cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);
|
|
11694
|
+
logger.log(` ${cmd}\n`);
|
|
11695
|
+
}
|
|
11696
|
+
function getWorkspaceUpgradeCommand(pm, packages) {
|
|
11697
|
+
const pkgList = packages.join(" ");
|
|
11698
|
+
switch (pm) {
|
|
11699
|
+
case "pnpm": return `pnpm update -r ${pkgList} --latest`;
|
|
11700
|
+
case "yarn": return `yarn upgrade ${pkgList}`;
|
|
11701
|
+
case "bun": return `bun update ${pkgList}`;
|
|
11702
|
+
case "npm": return `npm update ${pkgList} --workspaces`;
|
|
11703
|
+
default: return `npm update ${pkgList}`;
|
|
11704
|
+
}
|
|
11705
|
+
}
|
|
11706
|
+
function executeUpgrade(upgradable, pm, cwd) {
|
|
11707
|
+
const uniquePackages = [...new Set(upgradable.map((i) => i.packageName))];
|
|
11708
|
+
const cmd = getWorkspaceUpgradeCommand(pm, uniquePackages);
|
|
11709
|
+
logger.log(` Running: ${cmd}\n`);
|
|
11710
|
+
try {
|
|
11711
|
+
(0, node_child_process.execSync)(cmd, {
|
|
11712
|
+
cwd,
|
|
11713
|
+
stdio: "inherit",
|
|
11714
|
+
timeout: 12e4
|
|
11715
|
+
});
|
|
11716
|
+
} catch {
|
|
11717
|
+
throw new Error("Package upgrade failed. Check the output above for details.");
|
|
11718
|
+
}
|
|
11719
|
+
}
|
|
11720
|
+
|
|
11307
11721
|
//#endregion
|
|
11308
11722
|
//#region src/index.ts
|
|
11309
11723
|
const program = new commander.Command();
|
|
@@ -11318,6 +11732,16 @@ program.command("init").description("Scaffold a new project").argument("[name]",
|
|
|
11318
11732
|
process.exit(1);
|
|
11319
11733
|
}
|
|
11320
11734
|
});
|
|
11735
|
+
program.command("setup").description("Setup development environment (secrets, Docker, database)").option("--stage <stage>", "Stage name", "development").option("--force", "Regenerate secrets even if they exist").option("--skip-docker", "Skip starting Docker services").option("-y, --yes", "Skip prompts").action(async (options) => {
|
|
11736
|
+
try {
|
|
11737
|
+
const globalOptions = program.opts();
|
|
11738
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11739
|
+
await setupCommand(options);
|
|
11740
|
+
} catch (error) {
|
|
11741
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
11742
|
+
process.exit(1);
|
|
11743
|
+
}
|
|
11744
|
+
});
|
|
11321
11745
|
program.command("build").description("Build handlers from endpoints, functions, and crons").option("--provider <provider>", "Target provider for generated handlers (aws, server)").option("--providers <providers>", "[DEPRECATED] Use --provider instead. Target providers for generated handlers (comma-separated)").option("--enable-openapi", "Enable OpenAPI documentation generation for server builds").option("--production", "Build for production (no dev tools, bundled output)").option("--skip-bundle", "Skip bundling step in production build").option("--stage <stage>", "Inject encrypted secrets for deployment stage").action(async (options) => {
|
|
11322
11746
|
try {
|
|
11323
11747
|
const globalOptions = program.opts();
|
|
@@ -11514,6 +11938,40 @@ program.command("secrets:import").description("Import secrets from a JSON file")
|
|
|
11514
11938
|
process.exit(1);
|
|
11515
11939
|
}
|
|
11516
11940
|
});
|
|
11941
|
+
program.command("secrets:push").description("Push secrets to remote provider (SSM)").requiredOption("--stage <stage>", "Stage name").action(async (options) => {
|
|
11942
|
+
try {
|
|
11943
|
+
const globalOptions = program.opts();
|
|
11944
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11945
|
+
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11946
|
+
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-BxFB34zW.cjs"));
|
|
11947
|
+
const { workspace } = await loadWorkspaceConfig$1();
|
|
11948
|
+
await pushSecrets$1(options.stage, workspace);
|
|
11949
|
+
console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
|
|
11950
|
+
} catch (error) {
|
|
11951
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
11952
|
+
process.exit(1);
|
|
11953
|
+
}
|
|
11954
|
+
});
|
|
11955
|
+
program.command("secrets:pull").description("Pull secrets from remote provider (SSM)").requiredOption("--stage <stage>", "Stage name").action(async (options) => {
|
|
11956
|
+
try {
|
|
11957
|
+
const globalOptions = program.opts();
|
|
11958
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11959
|
+
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11960
|
+
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-BxFB34zW.cjs"));
|
|
11961
|
+
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11962
|
+
const { workspace } = await loadWorkspaceConfig$1();
|
|
11963
|
+
const secrets = await pullSecrets$1(options.stage, workspace);
|
|
11964
|
+
if (!secrets) {
|
|
11965
|
+
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
11966
|
+
process.exit(1);
|
|
11967
|
+
}
|
|
11968
|
+
await writeStageSecrets$1(secrets, workspace.root);
|
|
11969
|
+
console.log(`\n✓ Secrets pulled for stage "${options.stage}"`);
|
|
11970
|
+
} catch (error) {
|
|
11971
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
11972
|
+
process.exit(1);
|
|
11973
|
+
}
|
|
11974
|
+
});
|
|
11517
11975
|
program.command("deploy").description("Deploy application to a provider").requiredOption("--provider <provider>", "Deploy provider (docker, dokploy, aws-lambda)").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp)").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").action(async (options) => {
|
|
11518
11976
|
try {
|
|
11519
11977
|
const globalOptions = program.opts();
|
|
@@ -11660,6 +12118,16 @@ program.command("state:diff").description("Compare local and remote deployment s
|
|
|
11660
12118
|
process.exit(1);
|
|
11661
12119
|
}
|
|
11662
12120
|
});
|
|
12121
|
+
program.command("upgrade").description("Upgrade all @geekmidas packages to their latest versions").option("--dry-run", "Show what would be upgraded without making changes").action(async (options) => {
|
|
12122
|
+
try {
|
|
12123
|
+
const globalOptions = program.opts();
|
|
12124
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
12125
|
+
await upgradeCommand(options);
|
|
12126
|
+
} catch (error) {
|
|
12127
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
12128
|
+
process.exit(1);
|
|
12129
|
+
}
|
|
12130
|
+
});
|
|
11663
12131
|
program.parse();
|
|
11664
12132
|
|
|
11665
12133
|
//#endregion
|