@geekmidas/cli 1.10.2 → 1.10.4
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 +14 -0
- package/dist/{fullstack-secrets-COWz084x.cjs → fullstack-secrets-DqxYGrgW.cjs} +5 -5
- package/dist/fullstack-secrets-DqxYGrgW.cjs.map +1 -0
- package/dist/{fullstack-secrets-UZAFWuH4.mjs → fullstack-secrets-odm79Uo1.mjs} +5 -5
- package/dist/fullstack-secrets-odm79Uo1.mjs.map +1 -0
- package/dist/index.cjs +75 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +75 -14
- package/dist/index.mjs.map +1 -1
- package/dist/{reconcile-7yarEvmK.cjs → reconcile-CCtrj-zt.cjs} +2 -2
- package/dist/{reconcile-7yarEvmK.cjs.map → reconcile-CCtrj-zt.cjs.map} +1 -1
- package/dist/{reconcile-D2WCDQue.mjs → reconcile-WzC1oAUV.mjs} +2 -2
- package/dist/{reconcile-D2WCDQue.mjs.map → reconcile-WzC1oAUV.mjs.map} +1 -1
- package/package.json +3 -3
- package/src/dev/index.ts +23 -8
- package/src/init/generators/monorepo.ts +7 -0
- package/src/secrets/__tests__/generator.spec.ts +3 -3
- package/src/secrets/generator.ts +4 -4
- package/src/setup/__tests__/reconcile-secrets.spec.ts +248 -0
- package/src/setup/index.ts +46 -1
- package/src/test/__tests__/index.spec.ts +40 -1
- package/src/test/index.ts +23 -4
- package/dist/fullstack-secrets-COWz084x.cjs.map +0 -1
- package/dist/fullstack-secrets-UZAFWuH4.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @geekmidas/cli
|
|
2
2
|
|
|
3
|
+
## 1.10.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 🐛 [`6123575`](https://github.com/geekmidas/toolbox/commit/6123575f05ba5c8563413fffdad67d0e2880fb08) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix config hosts
|
|
8
|
+
|
|
9
|
+
- 🐛 [`96618ff`](https://github.com/geekmidas/toolbox/commit/96618ff36fd3248bfc29f4517fda79eea4a66dda) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix credentials loading for tests
|
|
10
|
+
|
|
11
|
+
## 1.10.3
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 🐛 [`6a92fa7`](https://github.com/geekmidas/toolbox/commit/6a92fa737057d77178a4d31480505013fbe033af) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix dev scripts and spawns also when canceling prcocess.
|
|
16
|
+
|
|
3
17
|
## 1.10.2
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -11,21 +11,21 @@ const node_crypto = require_chunk.__toESM(require("node:crypto"));
|
|
|
11
11
|
function generateSecurePassword(length = 32) {
|
|
12
12
|
return (0, node_crypto.randomBytes)(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
|
|
13
13
|
}
|
|
14
|
-
/** Default service configurations */
|
|
14
|
+
/** Default service configurations (localhost for local dev via Docker port mapping) */
|
|
15
15
|
const SERVICE_DEFAULTS = {
|
|
16
16
|
postgres: {
|
|
17
|
-
host: "
|
|
17
|
+
host: "localhost",
|
|
18
18
|
port: 5432,
|
|
19
19
|
username: "app",
|
|
20
20
|
database: "app"
|
|
21
21
|
},
|
|
22
22
|
redis: {
|
|
23
|
-
host: "
|
|
23
|
+
host: "localhost",
|
|
24
24
|
port: 6379,
|
|
25
25
|
username: "default"
|
|
26
26
|
},
|
|
27
27
|
rabbitmq: {
|
|
28
|
-
host: "
|
|
28
|
+
host: "localhost",
|
|
29
29
|
port: 5672,
|
|
30
30
|
username: "app",
|
|
31
31
|
vhost: "/"
|
|
@@ -235,4 +235,4 @@ Object.defineProperty(exports, 'writeDockerEnvFromSecrets', {
|
|
|
235
235
|
return writeDockerEnvFromSecrets;
|
|
236
236
|
}
|
|
237
237
|
});
|
|
238
|
-
//# sourceMappingURL=fullstack-secrets-
|
|
238
|
+
//# sourceMappingURL=fullstack-secrets-DqxYGrgW.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullstack-secrets-DqxYGrgW.cjs","names":["SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n>","service: ComposeServiceName","services: ComposeServiceName[]","result: StageSecrets['services']","creds: ServiceCredentials","services: StageSecrets['services']","urls: StageSecrets['urls']","stage: string","secrets: StageSecrets","newCreds: ServiceCredentials","appName: string","password: string","projectName: string","workspace: NormalizedWorkspace","customs: Record<string, string>","frontendPorts: number[]","upperName","secrets: StageSecrets","workspaceRoot: string"],"sources":["../src/secrets/generator.ts","../src/setup/fullstack-secrets.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport type { ComposeServiceName } from '../types';\nimport type { ServiceCredentials, StageSecrets } from './types';\n\n/**\n * Generate a secure random password using URL-safe base64 characters.\n * @param length Password length (default: 32)\n */\nexport function generateSecurePassword(length = 32): string {\n\treturn randomBytes(Math.ceil((length * 3) / 4))\n\t\t.toString('base64url')\n\t\t.slice(0, length);\n}\n\n/** Default service configurations (localhost for local dev via Docker port mapping) */\nconst SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n> = {\n\tpostgres: {\n\t\thost: 'localhost',\n\t\tport: 5432,\n\t\tusername: 'app',\n\t\tdatabase: 'app',\n\t},\n\tredis: {\n\t\thost: 'localhost',\n\t\tport: 6379,\n\t\tusername: 'default',\n\t},\n\trabbitmq: {\n\t\thost: 'localhost',\n\t\tport: 5672,\n\t\tusername: 'app',\n\t\tvhost: '/',\n\t},\n};\n\n/**\n * Generate credentials for a specific service.\n */\nexport function generateServiceCredentials(\n\tservice: ComposeServiceName,\n): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS[service];\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t};\n}\n\n/**\n * Generate credentials for multiple services.\n */\nexport function generateServicesCredentials(\n\tservices: ComposeServiceName[],\n): StageSecrets['services'] {\n\tconst result: StageSecrets['services'] = {};\n\n\tfor (const service of services) {\n\t\tresult[service] = generateServiceCredentials(service);\n\t}\n\n\treturn result;\n}\n\n/**\n * Generate connection URL for PostgreSQL.\n */\nexport function generatePostgresUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;\n}\n\n/**\n * Generate connection URL for Redis.\n */\nexport function generateRedisUrl(creds: ServiceCredentials): string {\n\tconst { password, host, port } = creds;\n\treturn `redis://:${encodeURIComponent(password)}@${host}:${port}`;\n}\n\n/**\n * Generate connection URL for RabbitMQ.\n */\nexport function generateRabbitmqUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, vhost } = creds;\n\tconst encodedVhost = encodeURIComponent(vhost ?? '/');\n\treturn `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;\n}\n\n/**\n * Generate connection URLs from service credentials.\n */\nexport function generateConnectionUrls(\n\tservices: StageSecrets['services'],\n): StageSecrets['urls'] {\n\tconst urls: StageSecrets['urls'] = {};\n\n\tif (services.postgres) {\n\t\turls.DATABASE_URL = generatePostgresUrl(services.postgres);\n\t}\n\n\tif (services.redis) {\n\t\turls.REDIS_URL = generateRedisUrl(services.redis);\n\t}\n\n\tif (services.rabbitmq) {\n\t\turls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);\n\t}\n\n\treturn urls;\n}\n\n/**\n * Create a new StageSecrets object with generated credentials.\n */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\tconst urls = generateConnectionUrls(serviceCredentials);\n\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tservices: serviceCredentials,\n\t\turls,\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Rotate password for a specific service.\n */\nexport function rotateServicePassword(\n\tsecrets: StageSecrets,\n\tservice: ComposeServiceName,\n): StageSecrets {\n\tconst currentCreds = secrets.services[service];\n\tif (!currentCreds) {\n\t\tthrow new Error(`Service \"${service}\" not configured in secrets`);\n\t}\n\n\tconst newCreds: ServiceCredentials = {\n\t\t...currentCreds,\n\t\tpassword: generateSecurePassword(),\n\t};\n\n\tconst newServices = {\n\t\t...secrets.services,\n\t\t[service]: newCreds,\n\t};\n\n\treturn {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tservices: newServices,\n\t\turls: generateConnectionUrls(newServices),\n\t};\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { generateSecurePassword } from '../secrets/generator.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/**\n * Generate a secure random password for database users.\n * Uses a combination of timestamp and random bytes for uniqueness.\n */\nexport function generateDbPassword(): string {\n\treturn `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Generate database URL for an app.\n * All apps connect to the same database, but use different users/schemas.\n */\nexport function generateDbUrl(\n\tappName: string,\n\tpassword: string,\n\tprojectName: string,\n\thost = 'localhost',\n\tport = 5432,\n): string {\n\tconst userName = appName.replace(/-/g, '_');\n\tconst dbName = `${projectName.replace(/-/g, '_')}_dev`;\n\treturn `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;\n}\n\n/**\n * Generate fullstack-aware custom secrets for a workspace.\n *\n * Generates:\n * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET\n * - Per-app database passwords and URLs for backend apps with db service\n * - Better-auth secrets for apps using the better-auth framework\n */\nexport function generateFullstackCustomSecrets(\n\tworkspace: NormalizedWorkspace,\n): Record<string, string> {\n\tconst hasDb = !!workspace.services.db;\n\tconst customs: Record<string, string> = {\n\t\tNODE_ENV: 'development',\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\tif (!hasDb) {\n\t\treturn customs;\n\t}\n\n\t// Collect all frontend ports for trusted origins\n\tconst frontendPorts: number[] = [];\n\n\tfor (const [appName, appConfig] of Object.entries(workspace.apps)) {\n\t\tif (appConfig.type === 'frontend') {\n\t\t\tfrontendPorts.push(appConfig.port);\n\t\t\tconst upperName = appName.toUpperCase();\n\t\t\tcustoms[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Backend apps with database: generate per-app DB passwords and URLs\n\t\tconst password = generateDbPassword();\n\t\tconst upperName = appName.toUpperCase();\n\n\t\tcustoms[`${upperName}_DATABASE_URL`] = generateDbUrl(\n\t\t\tappName,\n\t\t\tpassword,\n\t\t\tworkspace.name,\n\t\t);\n\t\tcustoms[`${upperName}_DB_PASSWORD`] = password;\n\n\t\t// Better-auth framework secrets\n\t\tif (appConfig.framework === 'better-auth') {\n\t\t\tcustoms.AUTH_PORT = String(appConfig.port);\n\t\t\tcustoms.AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t\tcustoms.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;\n\t\t\tcustoms.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t}\n\t}\n\n\t// Generate trusted origins for better-auth (all app ports)\n\tif (customs.BETTER_AUTH_SECRET) {\n\t\tconst allPorts = Object.values(workspace.apps).map((a) => a.port);\n\t\tcustoms.BETTER_AUTH_TRUSTED_ORIGINS = allPorts\n\t\t\t.map((p) => `http://localhost:${p}`)\n\t\t\t.join(',');\n\t}\n\n\treturn customs;\n}\n\n/**\n * Extract *_DB_PASSWORD keys from secrets and write docker/.env.\n *\n * The docker/.env file contains database passwords that the PostgreSQL\n * init script reads to create per-app database users.\n */\nexport async function writeDockerEnvFromSecrets(\n\tsecrets: StageSecrets,\n\tworkspaceRoot: string,\n): Promise<void> {\n\tconst dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) =>\n\t\tkey.endsWith('_DB_PASSWORD'),\n\t);\n\n\tif (dbPasswordEntries.length === 0) {\n\t\treturn;\n\t}\n\n\tconst envContent = `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join('\\n')}\n`;\n\n\tconst envPath = join(workspaceRoot, 'docker', '.env');\n\tawait mkdir(dirname(envPath), { recursive: true });\n\tawait writeFile(envPath, envContent);\n}\n"],"mappings":"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"}
|
|
@@ -10,21 +10,21 @@ import { randomBytes } from "node:crypto";
|
|
|
10
10
|
function generateSecurePassword(length = 32) {
|
|
11
11
|
return randomBytes(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
|
|
12
12
|
}
|
|
13
|
-
/** Default service configurations */
|
|
13
|
+
/** Default service configurations (localhost for local dev via Docker port mapping) */
|
|
14
14
|
const SERVICE_DEFAULTS = {
|
|
15
15
|
postgres: {
|
|
16
|
-
host: "
|
|
16
|
+
host: "localhost",
|
|
17
17
|
port: 5432,
|
|
18
18
|
username: "app",
|
|
19
19
|
database: "app"
|
|
20
20
|
},
|
|
21
21
|
redis: {
|
|
22
|
-
host: "
|
|
22
|
+
host: "localhost",
|
|
23
23
|
port: 6379,
|
|
24
24
|
username: "default"
|
|
25
25
|
},
|
|
26
26
|
rabbitmq: {
|
|
27
|
-
host: "
|
|
27
|
+
host: "localhost",
|
|
28
28
|
port: 5672,
|
|
29
29
|
username: "app",
|
|
30
30
|
vhost: "/"
|
|
@@ -199,4 +199,4 @@ ${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
|
|
|
199
199
|
|
|
200
200
|
//#endregion
|
|
201
201
|
export { createStageSecrets, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, rotateServicePassword, writeDockerEnvFromSecrets };
|
|
202
|
-
//# sourceMappingURL=fullstack-secrets-
|
|
202
|
+
//# sourceMappingURL=fullstack-secrets-odm79Uo1.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullstack-secrets-odm79Uo1.mjs","names":["SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n>","service: ComposeServiceName","services: ComposeServiceName[]","result: StageSecrets['services']","creds: ServiceCredentials","services: StageSecrets['services']","urls: StageSecrets['urls']","stage: string","secrets: StageSecrets","newCreds: ServiceCredentials","appName: string","password: string","projectName: string","workspace: NormalizedWorkspace","customs: Record<string, string>","frontendPorts: number[]","upperName","secrets: StageSecrets","workspaceRoot: string"],"sources":["../src/secrets/generator.ts","../src/setup/fullstack-secrets.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport type { ComposeServiceName } from '../types';\nimport type { ServiceCredentials, StageSecrets } from './types';\n\n/**\n * Generate a secure random password using URL-safe base64 characters.\n * @param length Password length (default: 32)\n */\nexport function generateSecurePassword(length = 32): string {\n\treturn randomBytes(Math.ceil((length * 3) / 4))\n\t\t.toString('base64url')\n\t\t.slice(0, length);\n}\n\n/** Default service configurations (localhost for local dev via Docker port mapping) */\nconst SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n> = {\n\tpostgres: {\n\t\thost: 'localhost',\n\t\tport: 5432,\n\t\tusername: 'app',\n\t\tdatabase: 'app',\n\t},\n\tredis: {\n\t\thost: 'localhost',\n\t\tport: 6379,\n\t\tusername: 'default',\n\t},\n\trabbitmq: {\n\t\thost: 'localhost',\n\t\tport: 5672,\n\t\tusername: 'app',\n\t\tvhost: '/',\n\t},\n};\n\n/**\n * Generate credentials for a specific service.\n */\nexport function generateServiceCredentials(\n\tservice: ComposeServiceName,\n): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS[service];\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t};\n}\n\n/**\n * Generate credentials for multiple services.\n */\nexport function generateServicesCredentials(\n\tservices: ComposeServiceName[],\n): StageSecrets['services'] {\n\tconst result: StageSecrets['services'] = {};\n\n\tfor (const service of services) {\n\t\tresult[service] = generateServiceCredentials(service);\n\t}\n\n\treturn result;\n}\n\n/**\n * Generate connection URL for PostgreSQL.\n */\nexport function generatePostgresUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;\n}\n\n/**\n * Generate connection URL for Redis.\n */\nexport function generateRedisUrl(creds: ServiceCredentials): string {\n\tconst { password, host, port } = creds;\n\treturn `redis://:${encodeURIComponent(password)}@${host}:${port}`;\n}\n\n/**\n * Generate connection URL for RabbitMQ.\n */\nexport function generateRabbitmqUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, vhost } = creds;\n\tconst encodedVhost = encodeURIComponent(vhost ?? '/');\n\treturn `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;\n}\n\n/**\n * Generate connection URLs from service credentials.\n */\nexport function generateConnectionUrls(\n\tservices: StageSecrets['services'],\n): StageSecrets['urls'] {\n\tconst urls: StageSecrets['urls'] = {};\n\n\tif (services.postgres) {\n\t\turls.DATABASE_URL = generatePostgresUrl(services.postgres);\n\t}\n\n\tif (services.redis) {\n\t\turls.REDIS_URL = generateRedisUrl(services.redis);\n\t}\n\n\tif (services.rabbitmq) {\n\t\turls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);\n\t}\n\n\treturn urls;\n}\n\n/**\n * Create a new StageSecrets object with generated credentials.\n */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\tconst urls = generateConnectionUrls(serviceCredentials);\n\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tservices: serviceCredentials,\n\t\turls,\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Rotate password for a specific service.\n */\nexport function rotateServicePassword(\n\tsecrets: StageSecrets,\n\tservice: ComposeServiceName,\n): StageSecrets {\n\tconst currentCreds = secrets.services[service];\n\tif (!currentCreds) {\n\t\tthrow new Error(`Service \"${service}\" not configured in secrets`);\n\t}\n\n\tconst newCreds: ServiceCredentials = {\n\t\t...currentCreds,\n\t\tpassword: generateSecurePassword(),\n\t};\n\n\tconst newServices = {\n\t\t...secrets.services,\n\t\t[service]: newCreds,\n\t};\n\n\treturn {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tservices: newServices,\n\t\turls: generateConnectionUrls(newServices),\n\t};\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { generateSecurePassword } from '../secrets/generator.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/**\n * Generate a secure random password for database users.\n * Uses a combination of timestamp and random bytes for uniqueness.\n */\nexport function generateDbPassword(): string {\n\treturn `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Generate database URL for an app.\n * All apps connect to the same database, but use different users/schemas.\n */\nexport function generateDbUrl(\n\tappName: string,\n\tpassword: string,\n\tprojectName: string,\n\thost = 'localhost',\n\tport = 5432,\n): string {\n\tconst userName = appName.replace(/-/g, '_');\n\tconst dbName = `${projectName.replace(/-/g, '_')}_dev`;\n\treturn `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;\n}\n\n/**\n * Generate fullstack-aware custom secrets for a workspace.\n *\n * Generates:\n * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET\n * - Per-app database passwords and URLs for backend apps with db service\n * - Better-auth secrets for apps using the better-auth framework\n */\nexport function generateFullstackCustomSecrets(\n\tworkspace: NormalizedWorkspace,\n): Record<string, string> {\n\tconst hasDb = !!workspace.services.db;\n\tconst customs: Record<string, string> = {\n\t\tNODE_ENV: 'development',\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\tif (!hasDb) {\n\t\treturn customs;\n\t}\n\n\t// Collect all frontend ports for trusted origins\n\tconst frontendPorts: number[] = [];\n\n\tfor (const [appName, appConfig] of Object.entries(workspace.apps)) {\n\t\tif (appConfig.type === 'frontend') {\n\t\t\tfrontendPorts.push(appConfig.port);\n\t\t\tconst upperName = appName.toUpperCase();\n\t\t\tcustoms[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Backend apps with database: generate per-app DB passwords and URLs\n\t\tconst password = generateDbPassword();\n\t\tconst upperName = appName.toUpperCase();\n\n\t\tcustoms[`${upperName}_DATABASE_URL`] = generateDbUrl(\n\t\t\tappName,\n\t\t\tpassword,\n\t\t\tworkspace.name,\n\t\t);\n\t\tcustoms[`${upperName}_DB_PASSWORD`] = password;\n\n\t\t// Better-auth framework secrets\n\t\tif (appConfig.framework === 'better-auth') {\n\t\t\tcustoms.AUTH_PORT = String(appConfig.port);\n\t\t\tcustoms.AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t\tcustoms.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;\n\t\t\tcustoms.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t}\n\t}\n\n\t// Generate trusted origins for better-auth (all app ports)\n\tif (customs.BETTER_AUTH_SECRET) {\n\t\tconst allPorts = Object.values(workspace.apps).map((a) => a.port);\n\t\tcustoms.BETTER_AUTH_TRUSTED_ORIGINS = allPorts\n\t\t\t.map((p) => `http://localhost:${p}`)\n\t\t\t.join(',');\n\t}\n\n\treturn customs;\n}\n\n/**\n * Extract *_DB_PASSWORD keys from secrets and write docker/.env.\n *\n * The docker/.env file contains database passwords that the PostgreSQL\n * init script reads to create per-app database users.\n */\nexport async function writeDockerEnvFromSecrets(\n\tsecrets: StageSecrets,\n\tworkspaceRoot: string,\n): Promise<void> {\n\tconst dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) =>\n\t\tkey.endsWith('_DB_PASSWORD'),\n\t);\n\n\tif (dbPasswordEntries.length === 0) {\n\t\treturn;\n\t}\n\n\tconst envContent = `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join('\\n')}\n`;\n\n\tconst envPath = join(workspaceRoot, 'docker', '.env');\n\tawait mkdir(dirname(envPath), { recursive: true });\n\tawait writeFile(envPath, envContent);\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ const require_storage = require('./storage-CoCNe0Pt.cjs');
|
|
|
8
8
|
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
|
-
const require_fullstack_secrets = require('./fullstack-secrets-
|
|
11
|
+
const require_fullstack_secrets = require('./fullstack-secrets-DqxYGrgW.cjs');
|
|
12
12
|
const require_openapi_react_query = require('./openapi-react-query-DYbBq-WJ.cjs');
|
|
13
13
|
const require_sync = require('./sync-DdkKaHqP.cjs');
|
|
14
14
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
@@ -35,7 +35,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
35
35
|
|
|
36
36
|
//#region package.json
|
|
37
37
|
var name = "@geekmidas/cli";
|
|
38
|
-
var version = "1.10.
|
|
38
|
+
var version = "1.10.3";
|
|
39
39
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
40
40
|
var private$1 = false;
|
|
41
41
|
var type = "module";
|
|
@@ -1345,7 +1345,8 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1345
1345
|
], {
|
|
1346
1346
|
cwd: workspace.root,
|
|
1347
1347
|
stdio: "inherit",
|
|
1348
|
-
env: turboEnv
|
|
1348
|
+
env: turboEnv,
|
|
1349
|
+
detached: true
|
|
1349
1350
|
});
|
|
1350
1351
|
let openApiWatcher = null;
|
|
1351
1352
|
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
@@ -1391,14 +1392,24 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1391
1392
|
isShuttingDown = true;
|
|
1392
1393
|
logger$11.log("\n🛑 Shutting down workspace...");
|
|
1393
1394
|
if (openApiWatcher) openApiWatcher.close().catch(() => {});
|
|
1394
|
-
|
|
1395
|
-
|
|
1395
|
+
const pid = turboProcess.pid;
|
|
1396
|
+
if (pid) try {
|
|
1397
|
+
process.kill(-pid, "SIGTERM");
|
|
1396
1398
|
} catch {
|
|
1397
|
-
|
|
1399
|
+
try {
|
|
1400
|
+
process.kill(pid, "SIGTERM");
|
|
1401
|
+
} catch {}
|
|
1398
1402
|
}
|
|
1399
1403
|
setTimeout(() => {
|
|
1404
|
+
if (pid) try {
|
|
1405
|
+
process.kill(-pid, "SIGKILL");
|
|
1406
|
+
} catch {
|
|
1407
|
+
try {
|
|
1408
|
+
process.kill(pid, "SIGKILL");
|
|
1409
|
+
} catch {}
|
|
1410
|
+
}
|
|
1400
1411
|
process.exit(0);
|
|
1401
|
-
},
|
|
1412
|
+
}, 3e3);
|
|
1402
1413
|
};
|
|
1403
1414
|
process.on("SIGINT", shutdown);
|
|
1404
1415
|
process.on("SIGTERM", shutdown);
|
|
@@ -7596,6 +7607,7 @@ export default defineWorkspace({
|
|
|
7596
7607
|
path: 'apps/auth',
|
|
7597
7608
|
port: 3002,
|
|
7598
7609
|
entry: './src/index.ts',
|
|
7610
|
+
framework: 'better-auth',
|
|
7599
7611
|
envParser: './src/config/env#envParser',
|
|
7600
7612
|
logger: './src/config/logger#logger',
|
|
7601
7613
|
},
|
|
@@ -7638,6 +7650,10 @@ export default defineWorkspace({
|
|
|
7638
7650
|
default: 'dokploy',
|
|
7639
7651
|
},`;
|
|
7640
7652
|
config += `
|
|
7653
|
+
secrets: {
|
|
7654
|
+
enabled: true,
|
|
7655
|
+
},`;
|
|
7656
|
+
config += `
|
|
7641
7657
|
});
|
|
7642
7658
|
`;
|
|
7643
7659
|
return config;
|
|
@@ -11216,7 +11232,11 @@ async function resolveSecrets(stage, workspace, options) {
|
|
|
11216
11232
|
if (require_storage.secretsExist(stage, workspace.root)) {
|
|
11217
11233
|
logger$1.log("🔐 Using existing local secrets");
|
|
11218
11234
|
const secrets = await require_storage.readStageSecrets(stage, workspace.root);
|
|
11219
|
-
if (secrets)
|
|
11235
|
+
if (secrets) {
|
|
11236
|
+
const reconciled = reconcileSecrets(secrets, workspace);
|
|
11237
|
+
if (reconciled) await require_storage.writeStageSecrets(reconciled, workspace.root);
|
|
11238
|
+
return reconciled ?? secrets;
|
|
11239
|
+
}
|
|
11220
11240
|
}
|
|
11221
11241
|
if (require_sync.isSSMConfigured(workspace)) {
|
|
11222
11242
|
logger$1.log("☁️ Checking for remote secrets in SSM...");
|
|
@@ -11236,6 +11256,29 @@ async function resolveSecrets(stage, workspace, options) {
|
|
|
11236
11256
|
return generateFreshSecrets(stage, workspace, options);
|
|
11237
11257
|
}
|
|
11238
11258
|
/**
|
|
11259
|
+
* Reconcile existing secrets with expected workspace-derived keys.
|
|
11260
|
+
* Adds missing keys (e.g. BETTER_AUTH_*) without overwriting existing values.
|
|
11261
|
+
* Returns the updated secrets if changes were made, or null if no changes needed.
|
|
11262
|
+
* @internal Exported for testing
|
|
11263
|
+
*/
|
|
11264
|
+
function reconcileSecrets(secrets, workspace) {
|
|
11265
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
11266
|
+
if (!isMultiApp) return null;
|
|
11267
|
+
const expected = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
|
|
11268
|
+
const missing = {};
|
|
11269
|
+
for (const [key, value] of Object.entries(expected)) if (!(key in secrets.custom)) missing[key] = value;
|
|
11270
|
+
if (Object.keys(missing).length === 0) return null;
|
|
11271
|
+
logger$1.log(` 🔄 Adding missing secrets: ${Object.keys(missing).join(", ")}`);
|
|
11272
|
+
return {
|
|
11273
|
+
...secrets,
|
|
11274
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11275
|
+
custom: {
|
|
11276
|
+
...secrets.custom,
|
|
11277
|
+
...missing
|
|
11278
|
+
}
|
|
11279
|
+
};
|
|
11280
|
+
}
|
|
11281
|
+
/**
|
|
11239
11282
|
* Generate fresh secrets for the workspace.
|
|
11240
11283
|
*/
|
|
11241
11284
|
async function generateFreshSecrets(stage, workspace, options) {
|
|
@@ -11346,6 +11389,24 @@ async function testCommand(options = {}) {
|
|
|
11346
11389
|
}
|
|
11347
11390
|
} catch {}
|
|
11348
11391
|
console.log("");
|
|
11392
|
+
const allSecrets = {
|
|
11393
|
+
...secretsEnv,
|
|
11394
|
+
...dependencyEnv
|
|
11395
|
+
};
|
|
11396
|
+
const gkmDir = (0, node_path.join)(cwd, ".gkm");
|
|
11397
|
+
await (0, node_fs_promises.mkdir)(gkmDir, { recursive: true });
|
|
11398
|
+
const secretsJsonPath = (0, node_path.join)(gkmDir, "test-secrets.json");
|
|
11399
|
+
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(allSecrets, null, 2));
|
|
11400
|
+
const preloadPath = (0, node_path.join)(gkmDir, "test-credentials-preload.ts");
|
|
11401
|
+
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
11402
|
+
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
11403
|
+
const tsxImport = "--import=tsx";
|
|
11404
|
+
const preloadImport = `--import=${preloadPath}`;
|
|
11405
|
+
const nodeOptions = [
|
|
11406
|
+
existingNodeOptions,
|
|
11407
|
+
tsxImport,
|
|
11408
|
+
preloadImport
|
|
11409
|
+
].filter(Boolean).join(" ");
|
|
11349
11410
|
const args = [];
|
|
11350
11411
|
if (options.run) args.push("run");
|
|
11351
11412
|
else if (options.watch) args.push("--watch");
|
|
@@ -11357,9 +11418,9 @@ async function testCommand(options = {}) {
|
|
|
11357
11418
|
stdio: "inherit",
|
|
11358
11419
|
env: {
|
|
11359
11420
|
...process.env,
|
|
11360
|
-
...
|
|
11361
|
-
|
|
11362
|
-
|
|
11421
|
+
...allSecrets,
|
|
11422
|
+
NODE_ENV: "test",
|
|
11423
|
+
NODE_OPTIONS: nodeOptions
|
|
11363
11424
|
}
|
|
11364
11425
|
});
|
|
11365
11426
|
return new Promise((resolve$3, reject) => {
|
|
@@ -11790,7 +11851,7 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
11790
11851
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11791
11852
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11792
11853
|
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
|
|
11793
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11854
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11794
11855
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11795
11856
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11796
11857
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
@@ -11816,7 +11877,7 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
11816
11877
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11817
11878
|
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
|
|
11818
11879
|
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11819
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11880
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11820
11881
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11821
11882
|
let secrets = await pullSecrets$1(options.stage, workspace);
|
|
11822
11883
|
if (!secrets) {
|
|
@@ -11841,7 +11902,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
11841
11902
|
const globalOptions = program.opts();
|
|
11842
11903
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11843
11904
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11844
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11905
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11845
11906
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11846
11907
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11847
11908
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|