@geekmidas/cli 1.10.26 → 1.10.28
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 +12 -0
- package/FUNCTION_CRON_SUPPORT.md +2 -2
- package/dist/{fullstack-secrets-C6jD5nlK.mjs → fullstack-secrets-BDE7Sk93.mjs} +7 -3
- package/dist/fullstack-secrets-BDE7Sk93.mjs.map +1 -0
- package/dist/{fullstack-secrets-BsO5GyPi.cjs → fullstack-secrets-CsxU84eN.cjs} +7 -3
- package/dist/fullstack-secrets-CsxU84eN.cjs.map +1 -0
- package/dist/index.cjs +42 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +42 -85
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-BXuCY14q.cjs → openapi-CBU4TEi-.cjs} +23 -130
- package/dist/openapi-CBU4TEi-.cjs.map +1 -0
- package/dist/{openapi-DeG0ffQ8.mjs → openapi-CQCoLuzh.mjs} +24 -113
- package/dist/openapi-CQCoLuzh.mjs.map +1 -0
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{reconcile-jfx0dsno.mjs → reconcile-BjWhjfSp.mjs} +2 -2
- package/dist/{reconcile-jfx0dsno.mjs.map → reconcile-BjWhjfSp.mjs.map} +1 -1
- package/dist/{reconcile-BJ_UqFLI.cjs → reconcile-DF8t4HC7.cjs} +2 -2
- package/dist/{reconcile-BJ_UqFLI.cjs.map → reconcile-DF8t4HC7.cjs.map} +1 -1
- package/package.json +3 -3
- package/src/credentials/index.ts +5 -4
- package/src/dev/index.ts +5 -100
- package/src/init/generators/docker.ts +8 -10
- package/src/init/generators/monorepo.ts +0 -3
- package/src/init/generators/package.ts +1 -4
- package/src/init/generators/web.ts +7 -3
- package/src/secrets/__tests__/generator.spec.ts +8 -3
- package/src/secrets/generator.ts +12 -4
- package/src/setup/__tests__/reconcile-secrets.spec.ts +11 -0
- package/src/setup/index.ts +20 -19
- package/dist/fullstack-secrets-BsO5GyPi.cjs.map +0 -1
- package/dist/fullstack-secrets-C6jD5nlK.mjs.map +0 -1
- package/dist/openapi-BXuCY14q.cjs.map +0 -1
- package/dist/openapi-DeG0ffQ8.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @geekmidas/cli
|
|
2
2
|
|
|
3
|
+
## 1.10.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 🔥 [`9e8f923`](https://github.com/geekmidas/toolbox/commit/9e8f9239798649bedeb16906ed83d0b71065c917) Thanks [@geekmidas](https://github.com/geekmidas)! - Remove client generation from cli
|
|
8
|
+
|
|
9
|
+
## 1.10.27
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 🐛 [`bead80b`](https://github.com/geekmidas/toolbox/commit/bead80b78437f616c593c521a39a22155de3c498) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix recociliation to have pg boss
|
|
14
|
+
|
|
3
15
|
## 1.10.26
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/FUNCTION_CRON_SUPPORT.md
CHANGED
|
@@ -58,7 +58,7 @@ For fine-grained control over providers:
|
|
|
58
58
|
Functions are standalone Lambda handlers that can be triggered by various AWS services:
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
61
|
-
import { f } from '@geekmidas/
|
|
61
|
+
import { f } from '@geekmidas/constructs/functions';
|
|
62
62
|
import { z } from 'zod';
|
|
63
63
|
|
|
64
64
|
export const processOrder = f
|
|
@@ -95,7 +95,7 @@ export const processOrder = f
|
|
|
95
95
|
Crons are scheduled functions that run on a regular basis:
|
|
96
96
|
|
|
97
97
|
```typescript
|
|
98
|
-
import { cron } from '@geekmidas/
|
|
98
|
+
import { cron } from '@geekmidas/constructs/crons';
|
|
99
99
|
|
|
100
100
|
// Using cron expression (runs daily at 9 AM UTC)
|
|
101
101
|
export const dailyReport = cron
|
|
@@ -168,6 +168,10 @@ function generateConnectionUrls(services, eventsBackend) {
|
|
|
168
168
|
const eventUrls = generateEventConnectionStrings(eventsBackend, services);
|
|
169
169
|
urls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;
|
|
170
170
|
urls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;
|
|
171
|
+
} else if (services.pgboss) {
|
|
172
|
+
const pgbossUrl = generatePgBossUrl(services.pgboss);
|
|
173
|
+
urls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;
|
|
174
|
+
urls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;
|
|
171
175
|
}
|
|
172
176
|
if (services.mailpit) {
|
|
173
177
|
urls.SMTP_HOST = services.mailpit.host;
|
|
@@ -204,14 +208,14 @@ function createStageSecrets(stage, services, options) {
|
|
|
204
208
|
serviceCredentials.minio.username = options.projectName;
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
|
-
|
|
208
|
-
if (eventsBackend === "pgboss" && serviceCredentials.postgres) serviceCredentials.pgboss = {
|
|
211
|
+
if (serviceCredentials.postgres && !serviceCredentials.pgboss) serviceCredentials.pgboss = {
|
|
209
212
|
...PGBOSS_DEFAULTS,
|
|
210
213
|
password: generateSecurePassword(),
|
|
211
214
|
host: serviceCredentials.postgres.host,
|
|
212
215
|
port: serviceCredentials.postgres.port,
|
|
213
216
|
database: serviceCredentials.postgres.database
|
|
214
217
|
};
|
|
218
|
+
const eventsBackend = options?.eventsBackend;
|
|
215
219
|
if (eventsBackend === "sns") serviceCredentials.localstack = generateLocalStackCredentials();
|
|
216
220
|
const urls = generateConnectionUrls(serviceCredentials, eventsBackend);
|
|
217
221
|
return {
|
|
@@ -327,4 +331,4 @@ ${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
|
|
|
327
331
|
|
|
328
332
|
//#endregion
|
|
329
333
|
export { createStageSecrets, generateConnectionUrls, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, generateLocalStackCredentials, generatePgBossUrl, generateSecurePassword, generateServiceCredentials, rotateServicePassword, writeDockerEnvFromSecrets };
|
|
330
|
-
//# sourceMappingURL=fullstack-secrets-
|
|
334
|
+
//# sourceMappingURL=fullstack-secrets-BDE7Sk93.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullstack-secrets-BDE7Sk93.mjs","names":["SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n>","PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'>","service: ComposeServiceName","services: ComposeServiceName[]","result: StageSecrets['services']","creds: ServiceCredentials","eventsBackend: EventsBackend","services: StageSecrets['services']","eventsBackend?: EventsBackend","urls: StageSecrets['urls']","stage: string","options?: { projectName?: string; eventsBackend?: EventsBackend }","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, EventsBackend } 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\tminio: {\n\t\thost: 'localhost',\n\t\tport: 9000,\n\t\tusername: 'app',\n\t\tbucket: 'app',\n\t},\n\tmailpit: {\n\t\thost: 'localhost',\n\t\tport: 1025,\n\t\tusername: 'app',\n\t},\n\tlocalstack: {\n\t\thost: 'localhost',\n\t\tport: 4566,\n\t\tusername: 'localstack',\n\t\tregion: 'us-east-1',\n\t},\n};\n\n/** Default credentials for pgboss (not a Docker service, reuses postgres) */\nconst PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'> = {\n\thost: 'localhost',\n\tport: 5432,\n\tusername: 'pgboss',\n\tdatabase: 'app',\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 endpoint URL for MinIO (S3-compatible).\n */\nexport function generateMinioEndpoint(creds: ServiceCredentials): string {\n\tconst { host, port } = creds;\n\treturn `http://${host}:${port}`;\n}\n\n/**\n * Generate a LocalStack-compatible access key ID.\n * Must start with 'LSIA' prefix and be at least 20 characters.\n * @see https://docs.localstack.cloud/aws/capabilities/config/credentials/\n */\nexport function generateLocalStackAccessKeyId(): string {\n\tconst suffix = randomBytes(12).toString('base64url').slice(0, 16);\n\treturn `LSIA${suffix}`;\n}\n\n/**\n * Generate connection URL for pg-boss (uses PostgreSQL protocol).\n * Format: pgboss://user:pass@host:port/db?schema=pgboss\n */\nexport function generatePgBossUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;\n}\n\n/**\n * Generate event connection strings based on the events backend.\n */\nexport function generateEventConnectionStrings(\n\teventsBackend: EventsBackend,\n\tservices: StageSecrets['services'],\n): { publisher: string; subscriber: string } {\n\tswitch (eventsBackend) {\n\t\tcase 'pgboss': {\n\t\t\tconst creds = services.pgboss;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('pgboss credentials required for pgboss events');\n\t\t\t}\n\t\t\tconst url = generatePgBossUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t\tcase 'sns': {\n\t\t\tconst creds = services.localstack;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('localstack credentials required for sns events');\n\t\t\t}\n\t\t\tconst endpoint = `http://${creds.host}:${creds.port}`;\n\t\t\tconst region = creds.region ?? 'us-east-1';\n\t\t\tconst accessKeyId = creds.accessKeyId ?? creds.username;\n\t\t\tconst secretKey = encodeURIComponent(creds.password);\n\t\t\treturn {\n\t\t\t\tpublisher: `sns://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t\tsubscriber: `sqs://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t};\n\t\t}\n\t\tcase 'rabbitmq': {\n\t\t\tconst creds = services.rabbitmq;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('rabbitmq credentials required for rabbitmq events');\n\t\t\t}\n\t\t\tconst url = generateRabbitmqUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t}\n}\n\n/**\n * Generate connection URLs from service credentials.\n */\nexport function generateConnectionUrls(\n\tservices: StageSecrets['services'],\n\teventsBackend?: EventsBackend,\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\tif (services.minio) {\n\t\turls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);\n\t}\n\n\tif (eventsBackend) {\n\t\tconst eventUrls = generateEventConnectionStrings(eventsBackend, services);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;\n\t} else if (services.pgboss) {\n\t\t// Default to pgboss when credentials exist but no explicit events backend\n\t\tconst pgbossUrl = generatePgBossUrl(services.pgboss);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;\n\t}\n\n\tif (services.mailpit) {\n\t\turls.SMTP_HOST = services.mailpit.host;\n\t\turls.SMTP_PORT = String(services.mailpit.port);\n\t}\n\n\treturn urls;\n}\n\n/**\n * Generate LocalStack service credentials with LSIA-prefixed access key.\n */\nexport function generateLocalStackCredentials(): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS.localstack;\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t\taccessKeyId: generateLocalStackAccessKeyId(),\n\t};\n}\n\n/**\n * Create a new StageSecrets object with generated credentials.\n * @param stage - The deployment stage (e.g., 'development', 'production')\n * @param services - List of services to generate credentials for\n * @param options - Optional configuration\n * @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')\n * @param options.eventsBackend - Event backend type (pgboss, sns, rabbitmq)\n */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n\toptions?: { projectName?: string; eventsBackend?: EventsBackend },\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\n\t// Override service defaults with project-derived names if provided\n\tif (options?.projectName) {\n\t\tif (serviceCredentials.postgres) {\n\t\t\tserviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;\n\t\t}\n\t\tif (serviceCredentials.minio) {\n\t\t\tserviceCredentials.minio.bucket = options.projectName;\n\t\t\tserviceCredentials.minio.username = options.projectName;\n\t\t}\n\t}\n\n\t// Always create pgboss credentials when postgres is available.\n\t// pgboss reuses postgres host/port/database but with a dedicated user,\n\t// so subscribers work out of the box without explicit events config.\n\tif (serviceCredentials.postgres && !serviceCredentials.pgboss) {\n\t\tserviceCredentials.pgboss = {\n\t\t\t...PGBOSS_DEFAULTS,\n\t\t\tpassword: generateSecurePassword(),\n\t\t\thost: serviceCredentials.postgres.host,\n\t\t\tport: serviceCredentials.postgres.port,\n\t\t\tdatabase: serviceCredentials.postgres.database,\n\t\t};\n\t}\n\n\t// Generate event-specific credentials\n\tconst eventsBackend = options?.eventsBackend;\n\tif (eventsBackend === 'sns') {\n\t\t// LocalStack credentials with LSIA-prefixed access key\n\t\tserviceCredentials.localstack = generateLocalStackCredentials();\n\t}\n\n\tconst urls = generateConnectionUrls(serviceCredentials, eventsBackend);\n\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\teventsBackend,\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, secrets.eventsBackend),\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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"}
|
|
@@ -169,6 +169,10 @@ function generateConnectionUrls(services, eventsBackend) {
|
|
|
169
169
|
const eventUrls = generateEventConnectionStrings(eventsBackend, services);
|
|
170
170
|
urls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;
|
|
171
171
|
urls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;
|
|
172
|
+
} else if (services.pgboss) {
|
|
173
|
+
const pgbossUrl = generatePgBossUrl(services.pgboss);
|
|
174
|
+
urls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;
|
|
175
|
+
urls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;
|
|
172
176
|
}
|
|
173
177
|
if (services.mailpit) {
|
|
174
178
|
urls.SMTP_HOST = services.mailpit.host;
|
|
@@ -205,14 +209,14 @@ function createStageSecrets(stage, services, options) {
|
|
|
205
209
|
serviceCredentials.minio.username = options.projectName;
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
|
-
|
|
209
|
-
if (eventsBackend === "pgboss" && serviceCredentials.postgres) serviceCredentials.pgboss = {
|
|
212
|
+
if (serviceCredentials.postgres && !serviceCredentials.pgboss) serviceCredentials.pgboss = {
|
|
210
213
|
...PGBOSS_DEFAULTS,
|
|
211
214
|
password: generateSecurePassword(),
|
|
212
215
|
host: serviceCredentials.postgres.host,
|
|
213
216
|
port: serviceCredentials.postgres.port,
|
|
214
217
|
database: serviceCredentials.postgres.database
|
|
215
218
|
};
|
|
219
|
+
const eventsBackend = options?.eventsBackend;
|
|
216
220
|
if (eventsBackend === "sns") serviceCredentials.localstack = generateLocalStackCredentials();
|
|
217
221
|
const urls = generateConnectionUrls(serviceCredentials, eventsBackend);
|
|
218
222
|
return {
|
|
@@ -393,4 +397,4 @@ Object.defineProperty(exports, 'writeDockerEnvFromSecrets', {
|
|
|
393
397
|
return writeDockerEnvFromSecrets;
|
|
394
398
|
}
|
|
395
399
|
});
|
|
396
|
-
//# sourceMappingURL=fullstack-secrets-
|
|
400
|
+
//# sourceMappingURL=fullstack-secrets-CsxU84eN.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullstack-secrets-CsxU84eN.cjs","names":["SERVICE_DEFAULTS: Record<\n\tComposeServiceName,\n\tOmit<ServiceCredentials, 'password'>\n>","PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'>","service: ComposeServiceName","services: ComposeServiceName[]","result: StageSecrets['services']","creds: ServiceCredentials","eventsBackend: EventsBackend","services: StageSecrets['services']","eventsBackend?: EventsBackend","urls: StageSecrets['urls']","stage: string","options?: { projectName?: string; eventsBackend?: EventsBackend }","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, EventsBackend } 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\tminio: {\n\t\thost: 'localhost',\n\t\tport: 9000,\n\t\tusername: 'app',\n\t\tbucket: 'app',\n\t},\n\tmailpit: {\n\t\thost: 'localhost',\n\t\tport: 1025,\n\t\tusername: 'app',\n\t},\n\tlocalstack: {\n\t\thost: 'localhost',\n\t\tport: 4566,\n\t\tusername: 'localstack',\n\t\tregion: 'us-east-1',\n\t},\n};\n\n/** Default credentials for pgboss (not a Docker service, reuses postgres) */\nconst PGBOSS_DEFAULTS: Omit<ServiceCredentials, 'password'> = {\n\thost: 'localhost',\n\tport: 5432,\n\tusername: 'pgboss',\n\tdatabase: 'app',\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 endpoint URL for MinIO (S3-compatible).\n */\nexport function generateMinioEndpoint(creds: ServiceCredentials): string {\n\tconst { host, port } = creds;\n\treturn `http://${host}:${port}`;\n}\n\n/**\n * Generate a LocalStack-compatible access key ID.\n * Must start with 'LSIA' prefix and be at least 20 characters.\n * @see https://docs.localstack.cloud/aws/capabilities/config/credentials/\n */\nexport function generateLocalStackAccessKeyId(): string {\n\tconst suffix = randomBytes(12).toString('base64url').slice(0, 16);\n\treturn `LSIA${suffix}`;\n}\n\n/**\n * Generate connection URL for pg-boss (uses PostgreSQL protocol).\n * Format: pgboss://user:pass@host:port/db?schema=pgboss\n */\nexport function generatePgBossUrl(creds: ServiceCredentials): string {\n\tconst { username, password, host, port, database } = creds;\n\treturn `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;\n}\n\n/**\n * Generate event connection strings based on the events backend.\n */\nexport function generateEventConnectionStrings(\n\teventsBackend: EventsBackend,\n\tservices: StageSecrets['services'],\n): { publisher: string; subscriber: string } {\n\tswitch (eventsBackend) {\n\t\tcase 'pgboss': {\n\t\t\tconst creds = services.pgboss;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('pgboss credentials required for pgboss events');\n\t\t\t}\n\t\t\tconst url = generatePgBossUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t\tcase 'sns': {\n\t\t\tconst creds = services.localstack;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('localstack credentials required for sns events');\n\t\t\t}\n\t\t\tconst endpoint = `http://${creds.host}:${creds.port}`;\n\t\t\tconst region = creds.region ?? 'us-east-1';\n\t\t\tconst accessKeyId = creds.accessKeyId ?? creds.username;\n\t\t\tconst secretKey = encodeURIComponent(creds.password);\n\t\t\treturn {\n\t\t\t\tpublisher: `sns://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t\tsubscriber: `sqs://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,\n\t\t\t};\n\t\t}\n\t\tcase 'rabbitmq': {\n\t\t\tconst creds = services.rabbitmq;\n\t\t\tif (!creds) {\n\t\t\t\tthrow new Error('rabbitmq credentials required for rabbitmq events');\n\t\t\t}\n\t\t\tconst url = generateRabbitmqUrl(creds);\n\t\t\treturn { publisher: url, subscriber: url };\n\t\t}\n\t}\n}\n\n/**\n * Generate connection URLs from service credentials.\n */\nexport function generateConnectionUrls(\n\tservices: StageSecrets['services'],\n\teventsBackend?: EventsBackend,\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\tif (services.minio) {\n\t\turls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);\n\t}\n\n\tif (eventsBackend) {\n\t\tconst eventUrls = generateEventConnectionStrings(eventsBackend, services);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;\n\t} else if (services.pgboss) {\n\t\t// Default to pgboss when credentials exist but no explicit events backend\n\t\tconst pgbossUrl = generatePgBossUrl(services.pgboss);\n\t\turls.EVENT_PUBLISHER_CONNECTION_STRING = pgbossUrl;\n\t\turls.EVENT_SUBSCRIBER_CONNECTION_STRING = pgbossUrl;\n\t}\n\n\tif (services.mailpit) {\n\t\turls.SMTP_HOST = services.mailpit.host;\n\t\turls.SMTP_PORT = String(services.mailpit.port);\n\t}\n\n\treturn urls;\n}\n\n/**\n * Generate LocalStack service credentials with LSIA-prefixed access key.\n */\nexport function generateLocalStackCredentials(): ServiceCredentials {\n\tconst defaults = SERVICE_DEFAULTS.localstack;\n\treturn {\n\t\t...defaults,\n\t\tpassword: generateSecurePassword(),\n\t\taccessKeyId: generateLocalStackAccessKeyId(),\n\t};\n}\n\n/**\n * Create a new StageSecrets object with generated credentials.\n * @param stage - The deployment stage (e.g., 'development', 'production')\n * @param services - List of services to generate credentials for\n * @param options - Optional configuration\n * @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')\n * @param options.eventsBackend - Event backend type (pgboss, sns, rabbitmq)\n */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n\toptions?: { projectName?: string; eventsBackend?: EventsBackend },\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\n\t// Override service defaults with project-derived names if provided\n\tif (options?.projectName) {\n\t\tif (serviceCredentials.postgres) {\n\t\t\tserviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;\n\t\t}\n\t\tif (serviceCredentials.minio) {\n\t\t\tserviceCredentials.minio.bucket = options.projectName;\n\t\t\tserviceCredentials.minio.username = options.projectName;\n\t\t}\n\t}\n\n\t// Always create pgboss credentials when postgres is available.\n\t// pgboss reuses postgres host/port/database but with a dedicated user,\n\t// so subscribers work out of the box without explicit events config.\n\tif (serviceCredentials.postgres && !serviceCredentials.pgboss) {\n\t\tserviceCredentials.pgboss = {\n\t\t\t...PGBOSS_DEFAULTS,\n\t\t\tpassword: generateSecurePassword(),\n\t\t\thost: serviceCredentials.postgres.host,\n\t\t\tport: serviceCredentials.postgres.port,\n\t\t\tdatabase: serviceCredentials.postgres.database,\n\t\t};\n\t}\n\n\t// Generate event-specific credentials\n\tconst eventsBackend = options?.eventsBackend;\n\tif (eventsBackend === 'sns') {\n\t\t// LocalStack credentials with LSIA-prefixed access key\n\t\tserviceCredentials.localstack = generateLocalStackCredentials();\n\t}\n\n\tconst urls = generateConnectionUrls(serviceCredentials, eventsBackend);\n\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\teventsBackend,\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, secrets.eventsBackend),\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
|
@@ -3,9 +3,9 @@ const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
|
3
3
|
const require_workspace = require('./workspace-4SP3Gx4Y.cjs');
|
|
4
4
|
const require_config = require('./config-D3ORuiUs.cjs');
|
|
5
5
|
const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
6
|
-
const require_fullstack_secrets = require('./fullstack-secrets-
|
|
6
|
+
const require_fullstack_secrets = require('./fullstack-secrets-CsxU84eN.cjs');
|
|
7
7
|
const require_storage = require('./storage-B1wvztiJ.cjs');
|
|
8
|
-
const require_openapi = require('./openapi-
|
|
8
|
+
const require_openapi = require('./openapi-CBU4TEi-.cjs');
|
|
9
9
|
const require_dokploy_api = require('./dokploy-api-Dvyq-LsM.cjs');
|
|
10
10
|
const require_encryption = require('./encryption-DgKS-Dv9.cjs');
|
|
11
11
|
const require_CachedStateProvider = require('./CachedStateProvider-CxsXOKkg.cjs');
|
|
@@ -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.27";
|
|
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";
|
|
@@ -781,10 +781,10 @@ async function prepareEntryCredentials(options) {
|
|
|
781
781
|
const depEnv = require_workspace.getDependencyEnvVars(appInfo.workspace, appInfo.appName);
|
|
782
782
|
Object.assign(credentials, depEnv);
|
|
783
783
|
}
|
|
784
|
-
if (!credentials.EVENT_PUBLISHER_CONNECTION_STRING && !credentials.EVENT_SUBSCRIBER_CONNECTION_STRING && credentials.
|
|
784
|
+
if (!credentials.EVENT_PUBLISHER_CONNECTION_STRING && !credentials.EVENT_SUBSCRIBER_CONNECTION_STRING && credentials.PGBOSS_DB_USER) {
|
|
785
785
|
const pgbossUrl = require_fullstack_secrets.generatePgBossUrl({
|
|
786
|
-
username: credentials.
|
|
787
|
-
password: credentials.
|
|
786
|
+
username: credentials.PGBOSS_DB_USER,
|
|
787
|
+
password: credentials.PGBOSS_DB_PASSWORD ?? "",
|
|
788
788
|
host: credentials.POSTGRES_HOST ?? "localhost",
|
|
789
789
|
port: Number(credentials.POSTGRES_PORT ?? "5432"),
|
|
790
790
|
database: credentials.POSTGRES_DB ?? "app"
|
|
@@ -1558,11 +1558,6 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1558
1558
|
if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
|
|
1559
1559
|
logger$11.log("✅ Frontend apps validated");
|
|
1560
1560
|
}
|
|
1561
|
-
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
1562
|
-
const clientResults = await require_openapi.copyAllClients(workspace);
|
|
1563
|
-
const copiedCount = clientResults.filter((r) => r.success).length;
|
|
1564
|
-
if (copiedCount > 0) logger$11.log(`\n📦 Copied ${copiedCount} API client(s)`);
|
|
1565
|
-
}
|
|
1566
1561
|
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
1567
1562
|
const rawSecrets = await loadDevSecrets(workspace);
|
|
1568
1563
|
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
|
|
@@ -1625,50 +1620,11 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1625
1620
|
env: turboEnv,
|
|
1626
1621
|
detached: true
|
|
1627
1622
|
});
|
|
1628
|
-
let openApiWatcher = null;
|
|
1629
|
-
if (frontendApps.length > 0 && backendApps.length > 0) {
|
|
1630
|
-
const openApiPaths = [];
|
|
1631
|
-
for (const [appName] of backendApps) {
|
|
1632
|
-
const openApiPath = require_openapi.getBackendOpenApiPath(workspace, appName);
|
|
1633
|
-
if (openApiPath) openApiPaths.push({
|
|
1634
|
-
path: openApiPath,
|
|
1635
|
-
appName
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
if (openApiPaths.length > 0) {
|
|
1639
|
-
logger$11.log(`\n👀 Watching ${openApiPaths.length} backend OpenAPI spec(s) for changes`);
|
|
1640
|
-
const pathToApp = new Map(openApiPaths.map((p) => [p.path, p.appName]));
|
|
1641
|
-
openApiWatcher = chokidar.default.watch(openApiPaths.map((p) => p.path), {
|
|
1642
|
-
persistent: true,
|
|
1643
|
-
ignoreInitial: true,
|
|
1644
|
-
depth: 0
|
|
1645
|
-
});
|
|
1646
|
-
let copyTimeout = null;
|
|
1647
|
-
const handleChange = async (changedPath) => {
|
|
1648
|
-
if (copyTimeout) clearTimeout(copyTimeout);
|
|
1649
|
-
copyTimeout = setTimeout(async () => {
|
|
1650
|
-
const backendAppName = pathToApp.get(changedPath);
|
|
1651
|
-
if (!backendAppName) return;
|
|
1652
|
-
logger$11.log(`\n🔄 OpenAPI spec changed for ${backendAppName}`);
|
|
1653
|
-
try {
|
|
1654
|
-
const results = await require_openapi.copyClientToFrontends(workspace, backendAppName, { silent: true });
|
|
1655
|
-
for (const result of results) if (result.success) logger$11.log(` 📦 Copied client to ${result.frontendApp} (${result.endpointCount} endpoints)`);
|
|
1656
|
-
else if (result.error) logger$11.error(` ❌ Failed to copy client to ${result.frontendApp}: ${result.error}`);
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
logger$11.error(` ❌ Failed to copy clients: ${error.message}`);
|
|
1659
|
-
}
|
|
1660
|
-
}, 200);
|
|
1661
|
-
};
|
|
1662
|
-
openApiWatcher.on("change", handleChange);
|
|
1663
|
-
openApiWatcher.on("add", handleChange);
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
1623
|
let isShuttingDown = false;
|
|
1667
1624
|
const shutdown = () => {
|
|
1668
1625
|
if (isShuttingDown) return;
|
|
1669
1626
|
isShuttingDown = true;
|
|
1670
1627
|
logger$11.log("\n🛑 Shutting down workspace...");
|
|
1671
|
-
if (openApiWatcher) openApiWatcher.close().catch(() => {});
|
|
1672
1628
|
const pid = turboProcess.pid;
|
|
1673
1629
|
if (pid) try {
|
|
1674
1630
|
process.kill(-pid, "SIGTERM");
|
|
@@ -1696,7 +1652,6 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1696
1652
|
reject(error);
|
|
1697
1653
|
});
|
|
1698
1654
|
turboProcess.on("exit", (code) => {
|
|
1699
|
-
if (openApiWatcher) openApiWatcher.close().catch(() => {});
|
|
1700
1655
|
if (code !== null && code !== 0) reject(new Error(`Turbo exited with code ${code}`));
|
|
1701
1656
|
else resolve$4();
|
|
1702
1657
|
});
|
|
@@ -7504,7 +7459,7 @@ function generateDockerEnv(apps, eventsBackend) {
|
|
|
7504
7459
|
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
7505
7460
|
return `${envVar}=${app.password}`;
|
|
7506
7461
|
});
|
|
7507
|
-
|
|
7462
|
+
envVars.push(`PGBOSS_DB_PASSWORD=pgboss-dev-password`);
|
|
7508
7463
|
return `# Auto-generated docker environment file
|
|
7509
7464
|
# Contains database passwords for docker-compose postgres init
|
|
7510
7465
|
# This file is gitignored - do not commit to version control
|
|
@@ -7565,8 +7520,7 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E
|
|
|
7565
7520
|
EOSQL
|
|
7566
7521
|
`;
|
|
7567
7522
|
});
|
|
7568
|
-
|
|
7569
|
-
if (eventsBackend === "pgboss") pgbossBlock = `
|
|
7523
|
+
const pgbossBlock = `
|
|
7570
7524
|
# Create pgboss user with dedicated schema - idempotent
|
|
7571
7525
|
echo "Creating pgboss user and schema..."
|
|
7572
7526
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
@@ -7602,7 +7556,8 @@ set -e
|
|
|
7602
7556
|
# Auto-generated PostgreSQL init script (idempotent - safe to re-run)
|
|
7603
7557
|
# Creates per-app users with separate schemas in a single database
|
|
7604
7558
|
# - api: uses public schema
|
|
7605
|
-
# - auth: uses auth schema (search_path=auth)
|
|
7559
|
+
# - auth: uses auth schema (search_path=auth)
|
|
7560
|
+
# - pgboss: uses pgboss schema for event processing
|
|
7606
7561
|
${extensions}
|
|
7607
7562
|
${userCreations.join("\n")}${pgbossBlock}
|
|
7608
7563
|
echo "Database initialization complete!"
|
|
@@ -8104,9 +8059,6 @@ export default defineWorkspace({
|
|
|
8104
8059
|
client: './src/config/client.ts',
|
|
8105
8060
|
server: './src/config/server.ts',
|
|
8106
8061
|
},
|
|
8107
|
-
client: {
|
|
8108
|
-
output: './src/api',
|
|
8109
|
-
},
|
|
8110
8062
|
},
|
|
8111
8063
|
},
|
|
8112
8064
|
shared: {
|
|
@@ -9179,10 +9131,7 @@ function generatePackageJson(options, template) {
|
|
|
9179
9131
|
version: "0.0.1",
|
|
9180
9132
|
private: true,
|
|
9181
9133
|
type: "module",
|
|
9182
|
-
exports: { "./client":
|
|
9183
|
-
types: OPENAPI_OUTPUT_PATH$1,
|
|
9184
|
-
import: OPENAPI_OUTPUT_PATH$1
|
|
9185
|
-
} },
|
|
9134
|
+
exports: { "./client": OPENAPI_OUTPUT_PATH$1 },
|
|
9186
9135
|
scripts: scripts$1,
|
|
9187
9136
|
dependencies: sortObject(dependencies$1),
|
|
9188
9137
|
devDependencies: sortObject(devDependencies$1)
|
|
@@ -10736,6 +10685,7 @@ storybook-static/
|
|
|
10736
10685
|
function generateWebAppFiles(options) {
|
|
10737
10686
|
if (!options.monorepo || options.template !== "fullstack") return [];
|
|
10738
10687
|
const packageName = `@${options.name}/web`;
|
|
10688
|
+
const apiPackage = `@${options.name}/api`;
|
|
10739
10689
|
const modelsPackage = `@${options.name}/models`;
|
|
10740
10690
|
const uiPackage = `@${options.name}/ui`;
|
|
10741
10691
|
const packageJson = {
|
|
@@ -10750,6 +10700,7 @@ function generateWebAppFiles(options) {
|
|
|
10750
10700
|
typecheck: "tsc --noEmit"
|
|
10751
10701
|
},
|
|
10752
10702
|
dependencies: {
|
|
10703
|
+
[apiPackage]: "workspace:*",
|
|
10753
10704
|
[modelsPackage]: "workspace:*",
|
|
10754
10705
|
[uiPackage]: "workspace:*",
|
|
10755
10706
|
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
@@ -10776,7 +10727,7 @@ function generateWebAppFiles(options) {
|
|
|
10776
10727
|
const nextConfig: NextConfig = {
|
|
10777
10728
|
output: 'standalone',
|
|
10778
10729
|
reactStrictMode: true,
|
|
10779
|
-
transpilePackages: ['${modelsPackage}', '${uiPackage}'],
|
|
10730
|
+
transpilePackages: ['${apiPackage}', '${modelsPackage}', '${uiPackage}'],
|
|
10780
10731
|
};
|
|
10781
10732
|
|
|
10782
10733
|
export default nextConfig;
|
|
@@ -10811,6 +10762,7 @@ export default nextConfig;
|
|
|
10811
10762
|
baseUrl: ".",
|
|
10812
10763
|
paths: {
|
|
10813
10764
|
"~/*": ["./src/*", "../../packages/ui/src/*"],
|
|
10765
|
+
[`${apiPackage}/client`]: ["../../apps/api/.gkm/openapi.ts"],
|
|
10814
10766
|
[`${modelsPackage}`]: ["../../packages/models/src"],
|
|
10815
10767
|
[`${modelsPackage}/*`]: ["../../packages/models/src/*"],
|
|
10816
10768
|
[`${uiPackage}`]: ["../../packages/ui/src"],
|
|
@@ -10824,7 +10776,11 @@ export default nextConfig;
|
|
|
10824
10776
|
".next/types/**/*.ts"
|
|
10825
10777
|
],
|
|
10826
10778
|
exclude: ["node_modules"],
|
|
10827
|
-
references: [
|
|
10779
|
+
references: [
|
|
10780
|
+
{ path: "../../apps/api" },
|
|
10781
|
+
{ path: "../../packages/ui" },
|
|
10782
|
+
{ path: "../../packages/models" }
|
|
10783
|
+
]
|
|
10828
10784
|
};
|
|
10829
10785
|
const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
|
|
10830
10786
|
|
|
@@ -10903,7 +10859,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
10903
10859
|
);
|
|
10904
10860
|
}
|
|
10905
10861
|
`;
|
|
10906
|
-
const apiIndexTs = `import { createApi } from '
|
|
10862
|
+
const apiIndexTs = `import { createApi } from '${apiPackage}/client';
|
|
10907
10863
|
import { getQueryClient } from '~/lib/query-client.ts';
|
|
10908
10864
|
import { clientConfig } from '~/config/client.ts';
|
|
10909
10865
|
|
|
@@ -11837,26 +11793,27 @@ function reconcileSecrets(secrets, workspace) {
|
|
|
11837
11793
|
logger$1.log(` 🔄 Adding missing service credentials: ${name$1}`);
|
|
11838
11794
|
changed = true;
|
|
11839
11795
|
}
|
|
11796
|
+
if (result.services.postgres && !result.services.pgboss) {
|
|
11797
|
+
result = {
|
|
11798
|
+
...result,
|
|
11799
|
+
services: {
|
|
11800
|
+
...result.services,
|
|
11801
|
+
pgboss: {
|
|
11802
|
+
host: result.services.postgres.host,
|
|
11803
|
+
port: result.services.postgres.port,
|
|
11804
|
+
username: "pgboss",
|
|
11805
|
+
password: require_fullstack_secrets.generateSecurePassword(),
|
|
11806
|
+
database: result.services.postgres.database ?? "app"
|
|
11807
|
+
}
|
|
11808
|
+
}
|
|
11809
|
+
};
|
|
11810
|
+
result.urls = require_fullstack_secrets.generateConnectionUrls(result.services, result.eventsBackend);
|
|
11811
|
+
logger$1.log(" 🔄 Adding missing service credentials: pgboss");
|
|
11812
|
+
changed = true;
|
|
11813
|
+
}
|
|
11840
11814
|
const eventsBackend = workspace.services.events;
|
|
11841
11815
|
if (eventsBackend && result.eventsBackend !== eventsBackend) {
|
|
11842
11816
|
result.eventsBackend = eventsBackend;
|
|
11843
|
-
if (eventsBackend === "pgboss" && !result.services.pgboss) {
|
|
11844
|
-
result = {
|
|
11845
|
-
...result,
|
|
11846
|
-
services: {
|
|
11847
|
-
...result.services,
|
|
11848
|
-
pgboss: {
|
|
11849
|
-
host: result.services.postgres?.host ?? "localhost",
|
|
11850
|
-
port: result.services.postgres?.port ?? 5432,
|
|
11851
|
-
username: "pgboss",
|
|
11852
|
-
password: require_fullstack_secrets.generateSecurePassword(),
|
|
11853
|
-
database: result.services.postgres?.database ?? "app"
|
|
11854
|
-
}
|
|
11855
|
-
}
|
|
11856
|
-
};
|
|
11857
|
-
logger$1.log(" 🔄 Adding missing service credentials: pgboss");
|
|
11858
|
-
changed = true;
|
|
11859
|
-
}
|
|
11860
11817
|
if (eventsBackend === "sns" && !result.services.localstack) {
|
|
11861
11818
|
result = {
|
|
11862
11819
|
...result,
|
|
@@ -12427,7 +12384,7 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
12427
12384
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
12428
12385
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
12429
12386
|
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-D1Pa30oV.cjs"));
|
|
12430
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
12387
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-DF8t4HC7.cjs"));
|
|
12431
12388
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
12432
12389
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
12433
12390
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
@@ -12453,7 +12410,7 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
12453
12410
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
12454
12411
|
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-D1Pa30oV.cjs"));
|
|
12455
12412
|
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
12456
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
12413
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-DF8t4HC7.cjs"));
|
|
12457
12414
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
12458
12415
|
let secrets = await pullSecrets$1(options.stage, workspace);
|
|
12459
12416
|
if (!secrets) {
|
|
@@ -12478,7 +12435,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
12478
12435
|
const globalOptions = program.opts();
|
|
12479
12436
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
12480
12437
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
12481
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
12438
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-DF8t4HC7.cjs"));
|
|
12482
12439
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
|
|
12483
12440
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
12484
12441
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|