@geekmidas/cli 1.10.9 → 1.10.11
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/dist/{bundler-kk_XJTRp.mjs → bundler-BvByD6tt.mjs} +2 -2
- package/dist/{bundler-kk_XJTRp.mjs.map → bundler-BvByD6tt.mjs.map} +1 -1
- package/dist/{bundler-Bm3Az_sv.cjs → bundler-wY-XZDl9.cjs} +2 -2
- package/dist/{bundler-Bm3Az_sv.cjs.map → bundler-wY-XZDl9.cjs.map} +1 -1
- package/dist/{fullstack-secrets-CtWIYuI0.cjs → fullstack-secrets-AUkUyeW0.cjs} +21 -3
- package/dist/fullstack-secrets-AUkUyeW0.cjs.map +1 -0
- package/dist/{fullstack-secrets-C2lbdbLZ.mjs → fullstack-secrets-Bnm_QeZr.mjs} +10 -4
- package/dist/fullstack-secrets-Bnm_QeZr.mjs.map +1 -0
- package/dist/index.cjs +93 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +93 -50
- package/dist/index.mjs.map +1 -1
- package/dist/{reconcile-BnM6FA6g.mjs → reconcile--yaWO052.mjs} +2 -2
- package/dist/{reconcile-BnM6FA6g.mjs.map → reconcile--yaWO052.mjs.map} +1 -1
- package/dist/{reconcile-D6u4HSg8.cjs → reconcile-oktp4i_4.cjs} +2 -2
- package/dist/{reconcile-D6u4HSg8.cjs.map → reconcile-oktp4i_4.cjs.map} +1 -1
- package/dist/{storage-D6BGLgWf.cjs → storage-B3aXQBpI.cjs} +1 -1
- package/dist/{storage-Cs13jkJ9.cjs → storage-CEisMvif.cjs} +6 -6
- package/dist/{storage-B7H2PPCS.mjs.map → storage-CEisMvif.cjs.map} +1 -1
- package/dist/{storage-C1FNm2EP.mjs → storage-D3lh8Xpq.mjs} +1 -1
- package/dist/{storage-B7H2PPCS.mjs → storage-Df-NFMY9.mjs} +6 -6
- package/dist/{storage-Cs13jkJ9.cjs.map → storage-Df-NFMY9.mjs.map} +1 -1
- package/dist/{sync-CyGe5f1I.mjs → sync-CIzj1CQe.mjs} +1 -1
- package/dist/{sync-oCqELfeA.cjs → sync-CcDvBUu4.cjs} +2 -2
- package/dist/{sync-oCqELfeA.cjs.map → sync-CcDvBUu4.cjs.map} +1 -1
- package/dist/{sync-CzXruMzP.mjs → sync-D-7NTKvV.mjs} +2 -2
- package/dist/{sync-CzXruMzP.mjs.map → sync-D-7NTKvV.mjs.map} +1 -1
- package/dist/sync-QNXT8x13.cjs +4 -0
- package/package.json +3 -3
- package/src/dev/index.ts +6 -1
- package/src/docker/__tests__/compose.spec.ts +24 -11
- package/src/docker/compose.ts +25 -21
- package/src/secrets/__tests__/generator.spec.ts +5 -5
- package/src/secrets/__tests__/storage.spec.ts +7 -7
- package/src/secrets/generator.ts +10 -4
- package/src/secrets/index.ts +4 -4
- package/src/secrets/storage.ts +5 -5
- package/src/secrets/types.ts +1 -1
- package/src/setup/__tests__/reconcile-secrets.spec.ts +59 -0
- package/src/setup/index.ts +56 -16
- package/dist/fullstack-secrets-C2lbdbLZ.mjs.map +0 -1
- package/dist/fullstack-secrets-CtWIYuI0.cjs.map +0 -1
- package/dist/sync-DLlwsrBs.cjs +0 -4
|
@@ -259,10 +259,10 @@ describe('generateConnectionUrls', () => {
|
|
|
259
259
|
expect(urls.DATABASE_URL).toBeDefined();
|
|
260
260
|
expect(urls.REDIS_URL).toBeDefined();
|
|
261
261
|
expect(urls.RABBITMQ_URL).toBeDefined();
|
|
262
|
-
expect(urls.
|
|
262
|
+
expect(urls.STORAGE_ENDPOINT).toBe('http://minio:9000');
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
-
it('should generate
|
|
265
|
+
it('should generate STORAGE_ENDPOINT for minio', () => {
|
|
266
266
|
const urls = generateConnectionUrls({
|
|
267
267
|
minio: {
|
|
268
268
|
host: 'localhost',
|
|
@@ -273,7 +273,7 @@ describe('generateConnectionUrls', () => {
|
|
|
273
273
|
},
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
-
expect(urls.
|
|
276
|
+
expect(urls.STORAGE_ENDPOINT).toBe('http://localhost:9000');
|
|
277
277
|
expect(urls.DATABASE_URL).toBeUndefined();
|
|
278
278
|
});
|
|
279
279
|
});
|
|
@@ -308,11 +308,11 @@ describe('createStageSecrets', () => {
|
|
|
308
308
|
expect(secrets.urls.RABBITMQ_URL).toBeDefined();
|
|
309
309
|
});
|
|
310
310
|
|
|
311
|
-
it('should generate
|
|
311
|
+
it('should generate STORAGE_ENDPOINT for minio', () => {
|
|
312
312
|
const secrets = createStageSecrets('production', ['minio']);
|
|
313
313
|
|
|
314
314
|
expect(secrets.services.minio).toBeDefined();
|
|
315
|
-
expect(secrets.urls.
|
|
315
|
+
expect(secrets.urls.STORAGE_ENDPOINT).toBe('http://localhost:9000');
|
|
316
316
|
});
|
|
317
317
|
});
|
|
318
318
|
|
|
@@ -353,19 +353,19 @@ describe('toEmbeddableSecrets', () => {
|
|
|
353
353
|
},
|
|
354
354
|
},
|
|
355
355
|
urls: {
|
|
356
|
-
|
|
356
|
+
STORAGE_ENDPOINT: 'http://localhost:9000',
|
|
357
357
|
},
|
|
358
358
|
custom: {},
|
|
359
359
|
};
|
|
360
360
|
|
|
361
361
|
const embeddable = toEmbeddableSecrets(secrets);
|
|
362
362
|
|
|
363
|
-
expect(embeddable.
|
|
364
|
-
expect(embeddable.
|
|
365
|
-
expect(embeddable.
|
|
366
|
-
expect(embeddable.
|
|
367
|
-
expect(embeddable.
|
|
368
|
-
expect(embeddable.
|
|
363
|
+
expect(embeddable.STORAGE_ENDPOINT).toBe('http://localhost:9000');
|
|
364
|
+
expect(embeddable.STORAGE_ACCESS_KEY_ID).toBe('myaccesskey');
|
|
365
|
+
expect(embeddable.STORAGE_SECRET_ACCESS_KEY).toBe('mysecretkey');
|
|
366
|
+
expect(embeddable.STORAGE_BUCKET).toBe('my-bucket');
|
|
367
|
+
expect(embeddable.STORAGE_REGION).toBe('eu-west-1');
|
|
368
|
+
expect(embeddable.STORAGE_FORCE_PATH_STYLE).toBe('true');
|
|
369
369
|
});
|
|
370
370
|
|
|
371
371
|
it('should handle all services and custom secrets together', () => {
|
package/src/secrets/generator.ts
CHANGED
|
@@ -124,7 +124,7 @@ export function generateConnectionUrls(
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
if (services.minio) {
|
|
127
|
-
urls.
|
|
127
|
+
urls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return urls;
|
|
@@ -145,9 +145,15 @@ export function createStageSecrets(
|
|
|
145
145
|
const now = new Date().toISOString();
|
|
146
146
|
const serviceCredentials = generateServicesCredentials(services);
|
|
147
147
|
|
|
148
|
-
// Override
|
|
149
|
-
if (options?.projectName
|
|
150
|
-
serviceCredentials.postgres
|
|
148
|
+
// Override service defaults with project-derived names if provided
|
|
149
|
+
if (options?.projectName) {
|
|
150
|
+
if (serviceCredentials.postgres) {
|
|
151
|
+
serviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;
|
|
152
|
+
}
|
|
153
|
+
if (serviceCredentials.minio) {
|
|
154
|
+
serviceCredentials.minio.bucket = options.projectName;
|
|
155
|
+
serviceCredentials.minio.username = options.projectName;
|
|
156
|
+
}
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
const urls = generateConnectionUrls(serviceCredentials);
|
package/src/secrets/index.ts
CHANGED
|
@@ -129,8 +129,8 @@ export async function secretsInitCommand(
|
|
|
129
129
|
if (secrets.urls.RABBITMQ_URL) {
|
|
130
130
|
logger.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
131
131
|
}
|
|
132
|
-
if (secrets.urls.
|
|
133
|
-
logger.log(`
|
|
132
|
+
if (secrets.urls.STORAGE_ENDPOINT) {
|
|
133
|
+
logger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
if (Object.keys(secrets.custom).length > 0) {
|
|
@@ -260,8 +260,8 @@ export async function secretsShowCommand(
|
|
|
260
260
|
` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`,
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
|
-
if (secrets.urls.
|
|
264
|
-
logger.log(`
|
|
263
|
+
if (secrets.urls.STORAGE_ENDPOINT) {
|
|
264
|
+
logger.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
// Show custom secrets
|
package/src/secrets/storage.ts
CHANGED
|
@@ -209,11 +209,11 @@ export function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {
|
|
|
209
209
|
RABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',
|
|
210
210
|
}),
|
|
211
211
|
...(secrets.services.minio && {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
212
|
+
STORAGE_ACCESS_KEY_ID: secrets.services.minio.username,
|
|
213
|
+
STORAGE_SECRET_ACCESS_KEY: secrets.services.minio.password,
|
|
214
|
+
STORAGE_BUCKET: secrets.services.minio.bucket ?? 'app',
|
|
215
|
+
STORAGE_REGION: 'eu-west-1',
|
|
216
|
+
STORAGE_FORCE_PATH_STYLE: 'true',
|
|
217
217
|
}),
|
|
218
218
|
};
|
|
219
219
|
}
|
package/src/secrets/types.ts
CHANGED
|
@@ -124,6 +124,65 @@ describe('reconcileSecrets', () => {
|
|
|
124
124
|
expect(result).toBeNull();
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
it('should add missing service credentials when workspace config adds a new service', () => {
|
|
128
|
+
const workspace = createWorkspace({
|
|
129
|
+
services: { db: true, storage: true },
|
|
130
|
+
});
|
|
131
|
+
// Secrets only have postgres, not minio
|
|
132
|
+
const secrets = createSecrets({
|
|
133
|
+
NODE_ENV: 'development',
|
|
134
|
+
PORT: '3000',
|
|
135
|
+
API_DATABASE_URL: 'postgresql://api:pass@localhost:5432/test_dev',
|
|
136
|
+
API_DB_PASSWORD: 'pass',
|
|
137
|
+
AUTH_DATABASE_URL: 'postgresql://auth:pass@localhost:5432/test_dev',
|
|
138
|
+
AUTH_DB_PASSWORD: 'pass',
|
|
139
|
+
WEB_URL: 'http://localhost:3002',
|
|
140
|
+
BETTER_AUTH_SECRET: 'existing',
|
|
141
|
+
BETTER_AUTH_URL: 'http://localhost:3001',
|
|
142
|
+
BETTER_AUTH_TRUSTED_ORIGINS:
|
|
143
|
+
'http://localhost:3000,http://localhost:3001,http://localhost:3002',
|
|
144
|
+
AUTH_PORT: '3001',
|
|
145
|
+
AUTH_URL: 'http://localhost:3001',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const result = reconcileSecrets(secrets, workspace);
|
|
149
|
+
|
|
150
|
+
expect(result).not.toBeNull();
|
|
151
|
+
expect(result!.services.minio).toBeDefined();
|
|
152
|
+
expect(result!.services.minio!.host).toBe('localhost');
|
|
153
|
+
expect(result!.services.minio!.port).toBe(9000);
|
|
154
|
+
expect(result!.services.minio!.bucket).toBe('test-project');
|
|
155
|
+
expect(result!.services.minio!.password).toHaveLength(32);
|
|
156
|
+
expect(result!.urls.STORAGE_ENDPOINT).toBe('http://localhost:9000');
|
|
157
|
+
// Existing postgres should be preserved
|
|
158
|
+
expect(result!.services.postgres).toEqual(secrets.services.postgres);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should not regenerate credentials for existing services', () => {
|
|
162
|
+
const workspace = createWorkspace({
|
|
163
|
+
services: { db: true, storage: true },
|
|
164
|
+
});
|
|
165
|
+
// Include ALL expected custom keys so reconcile has nothing to add
|
|
166
|
+
const expected = generateFullstackCustomSecrets(
|
|
167
|
+
createWorkspace({ services: { db: true, storage: true } }),
|
|
168
|
+
);
|
|
169
|
+
const secrets = createSecrets(expected);
|
|
170
|
+
// Add existing minio creds
|
|
171
|
+
secrets.services.minio = {
|
|
172
|
+
host: 'localhost',
|
|
173
|
+
port: 9000,
|
|
174
|
+
username: 'mykey',
|
|
175
|
+
password: 'mysecret',
|
|
176
|
+
bucket: 'my-bucket',
|
|
177
|
+
};
|
|
178
|
+
secrets.urls.STORAGE_ENDPOINT = 'http://localhost:9000';
|
|
179
|
+
|
|
180
|
+
const result = reconcileSecrets(secrets, workspace);
|
|
181
|
+
|
|
182
|
+
// No changes needed — all services and custom keys present
|
|
183
|
+
expect(result).toBeNull();
|
|
184
|
+
});
|
|
185
|
+
|
|
127
186
|
it('should return null for single-app workspaces', () => {
|
|
128
187
|
const workspace = createWorkspace({
|
|
129
188
|
apps: {
|
package/src/setup/index.ts
CHANGED
|
@@ -3,7 +3,11 @@ import { join } from 'node:path';
|
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import { loadWorkspaceConfig } from '../config.js';
|
|
5
5
|
import { resolveServicePorts, startWorkspaceServices } from '../dev/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createStageSecrets,
|
|
8
|
+
generateConnectionUrls,
|
|
9
|
+
generateServiceCredentials,
|
|
10
|
+
} from '../secrets/generator.js';
|
|
7
11
|
import {
|
|
8
12
|
readStageSecrets,
|
|
9
13
|
secretsExist,
|
|
@@ -153,32 +157,68 @@ export function reconcileSecrets(
|
|
|
153
157
|
secrets: StageSecrets,
|
|
154
158
|
workspace: NormalizedWorkspace,
|
|
155
159
|
): StageSecrets | null {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
let changed = false;
|
|
161
|
+
let result = { ...secrets };
|
|
162
|
+
|
|
163
|
+
// Reconcile service credentials: add missing services
|
|
164
|
+
const serviceMap: {
|
|
165
|
+
key: keyof typeof workspace.services;
|
|
166
|
+
name: ComposeServiceName;
|
|
167
|
+
}[] = [
|
|
168
|
+
{ key: 'db', name: 'postgres' },
|
|
169
|
+
{ key: 'cache', name: 'redis' },
|
|
170
|
+
{ key: 'storage', name: 'minio' },
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
for (const { key, name } of serviceMap) {
|
|
174
|
+
if (workspace.services[key] && !result.services[name]) {
|
|
175
|
+
const creds = generateServiceCredentials(name);
|
|
176
|
+
// Override defaults with project-derived names
|
|
177
|
+
if (name === 'minio') {
|
|
178
|
+
creds.bucket = workspace.name;
|
|
179
|
+
creds.username = workspace.name;
|
|
180
|
+
}
|
|
181
|
+
result = {
|
|
182
|
+
...result,
|
|
183
|
+
services: { ...result.services, [name]: creds },
|
|
184
|
+
};
|
|
185
|
+
result.urls = generateConnectionUrls(result.services);
|
|
186
|
+
logger.log(` 🔄 Adding missing service credentials: ${name}`);
|
|
187
|
+
changed = true;
|
|
188
|
+
}
|
|
159
189
|
}
|
|
160
190
|
|
|
161
|
-
|
|
162
|
-
const
|
|
191
|
+
// Reconcile custom secrets for multi-app workspaces
|
|
192
|
+
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
193
|
+
if (isMultiApp) {
|
|
194
|
+
const expected = generateFullstackCustomSecrets(workspace);
|
|
195
|
+
const missing: Record<string, string> = {};
|
|
163
196
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
197
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
198
|
+
if (!(key in result.custom)) {
|
|
199
|
+
missing[key] = value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (Object.keys(missing).length > 0) {
|
|
204
|
+
logger.log(
|
|
205
|
+
` 🔄 Adding missing secrets: ${Object.keys(missing).join(', ')}`,
|
|
206
|
+
);
|
|
207
|
+
result = {
|
|
208
|
+
...result,
|
|
209
|
+
custom: { ...result.custom, ...missing },
|
|
210
|
+
};
|
|
211
|
+
changed = true;
|
|
167
212
|
}
|
|
168
213
|
}
|
|
169
214
|
|
|
170
|
-
if (
|
|
215
|
+
if (!changed) {
|
|
171
216
|
return null;
|
|
172
217
|
}
|
|
173
218
|
|
|
174
|
-
logger.log(
|
|
175
|
-
` 🔄 Adding missing secrets: ${Object.keys(missing).join(', ')}`,
|
|
176
|
-
);
|
|
177
|
-
|
|
178
219
|
return {
|
|
179
|
-
...
|
|
220
|
+
...result,
|
|
180
221
|
updatedAt: new Date().toISOString(),
|
|
181
|
-
custom: { ...secrets.custom, ...missing },
|
|
182
222
|
};
|
|
183
223
|
}
|
|
184
224
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fullstack-secrets-C2lbdbLZ.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","options?: { projectName?: 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\tminio: {\n\t\thost: 'localhost',\n\t\tport: 9000,\n\t\tusername: 'app',\n\t\tbucket: 'app',\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 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 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\tif (services.minio) {\n\t\turls.S3_ENDPOINT = generateMinioEndpoint(services.minio);\n\t}\n\n\treturn urls;\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 */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n\toptions?: { projectName?: string },\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\n\t// Override postgres database name with project-derived name if provided\n\tif (options?.projectName && serviceCredentials.postgres) {\n\t\tserviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;\n\t}\n\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fullstack-secrets-CtWIYuI0.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","options?: { projectName?: 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\tminio: {\n\t\thost: 'localhost',\n\t\tport: 9000,\n\t\tusername: 'app',\n\t\tbucket: 'app',\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 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 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\tif (services.minio) {\n\t\turls.S3_ENDPOINT = generateMinioEndpoint(services.minio);\n\t}\n\n\treturn urls;\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 */\nexport function createStageSecrets(\n\tstage: string,\n\tservices: ComposeServiceName[],\n\toptions?: { projectName?: string },\n): StageSecrets {\n\tconst now = new Date().toISOString();\n\tconst serviceCredentials = generateServicesCredentials(services);\n\n\t// Override postgres database name with project-derived name if provided\n\tif (options?.projectName && serviceCredentials.postgres) {\n\t\tserviceCredentials.postgres.database = `${options.projectName.replace(/-/g, '_')}_dev`;\n\t}\n\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/sync-DLlwsrBs.cjs
DELETED