@geekmidas/cli 1.10.3 → 1.10.5
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 +46 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +46 -45
- 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/__tests__/index.spec.ts +35 -9
- package/src/dev/index.ts +18 -1
- package/src/docker/__tests__/compose.spec.ts +7 -5
- package/src/docker/compose.ts +4 -4
- package/src/init/generators/docker.ts +4 -4
- 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/test/__tests__/index.spec.ts +102 -35
- package/src/test/index.ts +23 -45
- 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.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 🐛 [`c97b9db`](https://github.com/geekmidas/toolbox/commit/c97b9db7cb66040b461cd3682f0b82ae2f24bd14) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix credentials embedding
|
|
8
|
+
|
|
9
|
+
## 1.10.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 🐛 [`6123575`](https://github.com/geekmidas/toolbox/commit/6123575f05ba5c8563413fffdad67d0e2880fb08) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix config hosts
|
|
14
|
+
|
|
15
|
+
- 🐛 [`96618ff`](https://github.com/geekmidas/toolbox/commit/96618ff36fd3248bfc29f4517fda79eea4a66dda) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix credentials loading for tests
|
|
16
|
+
|
|
3
17
|
## 1.10.3
|
|
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.4";
|
|
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";
|
|
@@ -815,13 +815,19 @@ function rewriteUrlsWithPorts(secrets, resolvedPorts) {
|
|
|
815
815
|
const { ports, mappings } = resolvedPorts;
|
|
816
816
|
const result = { ...secrets };
|
|
817
817
|
const portReplacements = [];
|
|
818
|
+
const serviceNames = /* @__PURE__ */ new Set();
|
|
818
819
|
for (const mapping of mappings) {
|
|
820
|
+
serviceNames.add(mapping.service);
|
|
819
821
|
const resolved = ports[mapping.envVar];
|
|
820
822
|
if (resolved !== void 0) portReplacements.push({
|
|
821
823
|
defaultPort: mapping.defaultPort,
|
|
822
824
|
resolvedPort: resolved
|
|
823
825
|
});
|
|
824
826
|
}
|
|
827
|
+
for (const [key, value] of Object.entries(result)) {
|
|
828
|
+
if (!key.endsWith("_HOST")) continue;
|
|
829
|
+
if (serviceNames.has(value)) result[key] = "localhost";
|
|
830
|
+
}
|
|
825
831
|
for (const [key, value] of Object.entries(result)) {
|
|
826
832
|
if (!key.endsWith("_PORT")) continue;
|
|
827
833
|
for (const { defaultPort, resolvedPort } of portReplacements) if (value === String(defaultPort)) result[key] = String(resolvedPort);
|
|
@@ -829,6 +835,7 @@ function rewriteUrlsWithPorts(secrets, resolvedPorts) {
|
|
|
829
835
|
for (const [key, value] of Object.entries(result)) {
|
|
830
836
|
if (!key.endsWith("_URL") && key !== "DATABASE_URL") continue;
|
|
831
837
|
let rewritten = value;
|
|
838
|
+
for (const name$1 of serviceNames) rewritten = rewritten.replace(new RegExp(`@${name$1}:`, "g"), "@localhost:");
|
|
832
839
|
for (const { defaultPort, resolvedPort } of portReplacements) rewritten = replacePortInUrl(rewritten, defaultPort, resolvedPort);
|
|
833
840
|
result[key] = rewritten;
|
|
834
841
|
}
|
|
@@ -2882,7 +2889,7 @@ services:
|
|
|
2882
2889
|
environment:
|
|
2883
2890
|
- NODE_ENV=production
|
|
2884
2891
|
`;
|
|
2885
|
-
if (serviceMap.has("postgres")) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql
|
|
2892
|
+
if (serviceMap.has("postgres")) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
2886
2893
|
`;
|
|
2887
2894
|
if (serviceMap.has("redis")) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
2888
2895
|
`;
|
|
@@ -2917,7 +2924,7 @@ services:
|
|
|
2917
2924
|
volumes:
|
|
2918
2925
|
- postgres_data:/var/lib/postgresql/data
|
|
2919
2926
|
healthcheck:
|
|
2920
|
-
test: ["CMD-SHELL", "pg_isready -U
|
|
2927
|
+
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
2921
2928
|
interval: 5s
|
|
2922
2929
|
timeout: 5s
|
|
2923
2930
|
retries: 5
|
|
@@ -3046,7 +3053,7 @@ services:
|
|
|
3046
3053
|
volumes:
|
|
3047
3054
|
- postgres_data:/var/lib/postgresql/data
|
|
3048
3055
|
healthcheck:
|
|
3049
|
-
test: ["CMD-SHELL", "pg_isready -U
|
|
3056
|
+
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
3050
3057
|
interval: 5s
|
|
3051
3058
|
timeout: 5s
|
|
3052
3059
|
retries: 5
|
|
@@ -3139,7 +3146,7 @@ function generateAppService(appName, app, allApps, options) {
|
|
|
3139
3146
|
`;
|
|
3140
3147
|
}
|
|
3141
3148
|
if (app.type === "backend") {
|
|
3142
|
-
if (hasPostgres) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql
|
|
3149
|
+
if (hasPostgres) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
3143
3150
|
`;
|
|
3144
3151
|
if (hasRedis) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
3145
3152
|
`;
|
|
@@ -6951,15 +6958,15 @@ function generateDockerFiles(options, template, dbApps) {
|
|
|
6951
6958
|
container_name: ${options.name}-postgres
|
|
6952
6959
|
restart: unless-stopped${envFile}
|
|
6953
6960
|
environment:
|
|
6954
|
-
POSTGRES_USER: postgres
|
|
6955
|
-
POSTGRES_PASSWORD: postgres
|
|
6956
|
-
POSTGRES_DB:
|
|
6961
|
+
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
6962
|
+
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
6963
|
+
POSTGRES_DB: \${POSTGRES_DB:-${options.name.replace(/-/g, "_")}_dev}
|
|
6957
6964
|
ports:
|
|
6958
6965
|
- '\${POSTGRES_HOST_PORT:-5432}:5432'
|
|
6959
6966
|
volumes:
|
|
6960
6967
|
- postgres_data:/var/lib/postgresql/data${initVolume}
|
|
6961
6968
|
healthcheck:
|
|
6962
|
-
test: ['CMD-SHELL', 'pg_isready -U
|
|
6969
|
+
test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER']
|
|
6963
6970
|
interval: 5s
|
|
6964
6971
|
timeout: 5s
|
|
6965
6972
|
retries: 5`);
|
|
@@ -7607,6 +7614,7 @@ export default defineWorkspace({
|
|
|
7607
7614
|
path: 'apps/auth',
|
|
7608
7615
|
port: 3002,
|
|
7609
7616
|
entry: './src/index.ts',
|
|
7617
|
+
framework: 'better-auth',
|
|
7610
7618
|
envParser: './src/config/env#envParser',
|
|
7611
7619
|
logger: './src/config/logger#logger',
|
|
7612
7620
|
},
|
|
@@ -7649,6 +7657,10 @@ export default defineWorkspace({
|
|
|
7649
7657
|
default: 'dokploy',
|
|
7650
7658
|
},`;
|
|
7651
7659
|
config += `
|
|
7660
|
+
secrets: {
|
|
7661
|
+
enabled: true,
|
|
7662
|
+
},`;
|
|
7663
|
+
config += `
|
|
7652
7664
|
});
|
|
7653
7665
|
`;
|
|
7654
7666
|
return config;
|
|
@@ -11363,7 +11375,6 @@ async function testCommand(options = {}) {
|
|
|
11363
11375
|
}
|
|
11364
11376
|
}
|
|
11365
11377
|
secretsEnv = rewriteDatabaseUrlForTests(secretsEnv);
|
|
11366
|
-
await ensureTestDatabase(secretsEnv);
|
|
11367
11378
|
let dependencyEnv = {};
|
|
11368
11379
|
try {
|
|
11369
11380
|
const appInfo = await require_config.loadWorkspaceAppInfo(cwd);
|
|
@@ -11384,6 +11395,24 @@ async function testCommand(options = {}) {
|
|
|
11384
11395
|
}
|
|
11385
11396
|
} catch {}
|
|
11386
11397
|
console.log("");
|
|
11398
|
+
const allSecrets = {
|
|
11399
|
+
...secretsEnv,
|
|
11400
|
+
...dependencyEnv
|
|
11401
|
+
};
|
|
11402
|
+
const gkmDir = (0, node_path.join)(cwd, ".gkm");
|
|
11403
|
+
await (0, node_fs_promises.mkdir)(gkmDir, { recursive: true });
|
|
11404
|
+
const secretsJsonPath = (0, node_path.join)(gkmDir, "test-secrets.json");
|
|
11405
|
+
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(allSecrets, null, 2));
|
|
11406
|
+
const preloadPath = (0, node_path.join)(gkmDir, "test-credentials-preload.ts");
|
|
11407
|
+
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
11408
|
+
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
11409
|
+
const tsxImport = "--import=tsx";
|
|
11410
|
+
const preloadImport = `--import=${preloadPath}`;
|
|
11411
|
+
const nodeOptions = [
|
|
11412
|
+
existingNodeOptions,
|
|
11413
|
+
tsxImport,
|
|
11414
|
+
preloadImport
|
|
11415
|
+
].filter(Boolean).join(" ");
|
|
11387
11416
|
const args = [];
|
|
11388
11417
|
if (options.run) args.push("run");
|
|
11389
11418
|
else if (options.watch) args.push("--watch");
|
|
@@ -11395,9 +11424,9 @@ async function testCommand(options = {}) {
|
|
|
11395
11424
|
stdio: "inherit",
|
|
11396
11425
|
env: {
|
|
11397
11426
|
...process.env,
|
|
11398
|
-
...
|
|
11399
|
-
|
|
11400
|
-
|
|
11427
|
+
...allSecrets,
|
|
11428
|
+
NODE_ENV: "test",
|
|
11429
|
+
NODE_OPTIONS: nodeOptions
|
|
11401
11430
|
}
|
|
11402
11431
|
});
|
|
11403
11432
|
return new Promise((resolve$3, reject) => {
|
|
@@ -11433,34 +11462,6 @@ function rewriteDatabaseUrlForTests(env) {
|
|
|
11433
11462
|
}
|
|
11434
11463
|
return result;
|
|
11435
11464
|
}
|
|
11436
|
-
/**
|
|
11437
|
-
* Ensure the test database exists by connecting to the default database
|
|
11438
|
-
* and running CREATE DATABASE IF NOT EXISTS.
|
|
11439
|
-
* @internal Exported for testing
|
|
11440
|
-
*/
|
|
11441
|
-
async function ensureTestDatabase(env) {
|
|
11442
|
-
const databaseUrl = env.DATABASE_URL;
|
|
11443
|
-
if (!databaseUrl) return;
|
|
11444
|
-
try {
|
|
11445
|
-
const url = new URL(databaseUrl);
|
|
11446
|
-
const testDbName = url.pathname.slice(1);
|
|
11447
|
-
if (!testDbName) return;
|
|
11448
|
-
url.pathname = "/postgres";
|
|
11449
|
-
const { default: pg$1 } = await import("pg");
|
|
11450
|
-
const client = new pg$1.Client({ connectionString: url.toString() });
|
|
11451
|
-
await client.connect();
|
|
11452
|
-
try {
|
|
11453
|
-
await client.query(`CREATE DATABASE "${testDbName}"`);
|
|
11454
|
-
console.log(` 📦 Created test database "${testDbName}"`);
|
|
11455
|
-
} catch (err) {
|
|
11456
|
-
if (err.code !== "42P04") throw err;
|
|
11457
|
-
} finally {
|
|
11458
|
-
await client.end();
|
|
11459
|
-
}
|
|
11460
|
-
} catch (err) {
|
|
11461
|
-
console.log(` ⚠️ Could not ensure test database: ${err.message}`);
|
|
11462
|
-
}
|
|
11463
|
-
}
|
|
11464
11465
|
|
|
11465
11466
|
//#endregion
|
|
11466
11467
|
//#region src/upgrade/index.ts
|
|
@@ -11828,7 +11829,7 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
11828
11829
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11829
11830
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11830
11831
|
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
|
|
11831
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11832
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11832
11833
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11833
11834
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11834
11835
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
@@ -11854,7 +11855,7 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
11854
11855
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11855
11856
|
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
|
|
11856
11857
|
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11857
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11858
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11858
11859
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11859
11860
|
let secrets = await pullSecrets$1(options.stage, workspace);
|
|
11860
11861
|
if (!secrets) {
|
|
@@ -11879,7 +11880,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
11879
11880
|
const globalOptions = program.opts();
|
|
11880
11881
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11881
11882
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11882
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11883
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CCtrj-zt.cjs"));
|
|
11883
11884
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
|
|
11884
11885
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11885
11886
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|