@geekmidas/cli 1.9.0 → 1.9.1
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 +6 -0
- package/dist/{HostingerProvider-CEsQbmpY.cjs → HostingerProvider-5KYmwoK2.cjs} +1 -1
- package/dist/{HostingerProvider-CEsQbmpY.cjs.map → HostingerProvider-5KYmwoK2.cjs.map} +1 -1
- package/dist/{HostingerProvider-DkahM5AP.mjs → HostingerProvider-ANWchdiK.mjs} +1 -1
- package/dist/{HostingerProvider-DkahM5AP.mjs.map → HostingerProvider-ANWchdiK.mjs.map} +1 -1
- package/dist/{LocalStateProvider-Roi202l7.cjs → LocalStateProvider-CLifRC0Y.cjs} +1 -1
- package/dist/{LocalStateProvider-Roi202l7.cjs.map → LocalStateProvider-CLifRC0Y.cjs.map} +1 -1
- package/dist/{LocalStateProvider-DXIwWb7k.mjs → LocalStateProvider-Dp0KkRcw.mjs} +1 -1
- package/dist/{LocalStateProvider-DXIwWb7k.mjs.map → LocalStateProvider-Dp0KkRcw.mjs.map} +1 -1
- package/dist/{Route53Provider-Ckq_n5Be.mjs → Route53Provider-QoPgcXxn.mjs} +1 -1
- package/dist/{Route53Provider-Ckq_n5Be.mjs.map → Route53Provider-QoPgcXxn.mjs.map} +1 -1
- package/dist/{Route53Provider-BqXeHzuc.cjs → Route53Provider-owQQ4pn6.cjs} +1 -1
- package/dist/{Route53Provider-BqXeHzuc.cjs.map → Route53Provider-owQQ4pn6.cjs.map} +1 -1
- package/dist/{SSMStateProvider-BReQA5re.cjs → SSMStateProvider-CT8tjl9o.cjs} +1 -1
- package/dist/{SSMStateProvider-BReQA5re.cjs.map → SSMStateProvider-CT8tjl9o.cjs.map} +1 -1
- package/dist/{SSMStateProvider-wddd0_-d.mjs → SSMStateProvider-CksOTB8M.mjs} +1 -1
- package/dist/{SSMStateProvider-wddd0_-d.mjs.map → SSMStateProvider-CksOTB8M.mjs.map} +1 -1
- package/dist/{backup-provisioner-BAExdDtc.mjs → backup-provisioner-BEXoHTuC.mjs} +1 -1
- package/dist/{backup-provisioner-BAExdDtc.mjs.map → backup-provisioner-BEXoHTuC.mjs.map} +1 -1
- package/dist/{backup-provisioner-C8VK63I-.cjs → backup-provisioner-C4noe75O.cjs} +1 -1
- package/dist/{backup-provisioner-C8VK63I-.cjs.map → backup-provisioner-C4noe75O.cjs.map} +1 -1
- package/dist/{bundler-BxHyDhdt.mjs → bundler-DQYjKFPm.mjs} +1 -1
- package/dist/{bundler-BxHyDhdt.mjs.map → bundler-DQYjKFPm.mjs.map} +1 -1
- package/dist/{bundler-CuMIfXw5.cjs → bundler-NpfYPBUo.cjs} +1 -1
- package/dist/{bundler-CuMIfXw5.cjs.map → bundler-NpfYPBUo.cjs.map} +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/fullstack-secrets-COWz084x.cjs +238 -0
- package/dist/fullstack-secrets-COWz084x.cjs.map +1 -0
- package/dist/fullstack-secrets-UZAFWuH4.mjs +202 -0
- package/dist/fullstack-secrets-UZAFWuH4.mjs.map +1 -0
- package/dist/{index-BVNXOydm.d.mts → index-Bt2kX0-R.d.mts} +2 -2
- package/dist/{index-BVNXOydm.d.mts.map → index-Bt2kX0-R.d.mts.map} +1 -1
- package/dist/index.cjs +141 -276
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +128 -263
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-react-query-DaTMSPD5.mjs → openapi-react-query-C4UdILaI.mjs} +1 -1
- package/dist/{openapi-react-query-DaTMSPD5.mjs.map → openapi-react-query-C4UdILaI.mjs.map} +1 -1
- package/dist/{openapi-react-query-BeXvk-wa.cjs → openapi-react-query-DYbBq-WJ.cjs} +1 -1
- package/dist/{openapi-react-query-BeXvk-wa.cjs.map → openapi-react-query-DYbBq-WJ.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/reconcile-7yarEvmK.cjs +36 -0
- package/dist/reconcile-7yarEvmK.cjs.map +1 -0
- package/dist/reconcile-D2WCDQue.mjs +36 -0
- package/dist/reconcile-D2WCDQue.mjs.map +1 -0
- package/dist/{sync-BnqNNc6O.mjs → sync-6FoT41G3.mjs} +1 -1
- package/dist/{sync-CHfhmXF3.mjs → sync-CbeKrnQV.mjs} +1 -1
- package/dist/{sync-CHfhmXF3.mjs.map → sync-CbeKrnQV.mjs.map} +1 -1
- package/dist/{sync-BOS0jKLn.cjs → sync-DdkKaHqP.cjs} +1 -1
- package/dist/{sync-BOS0jKLn.cjs.map → sync-DdkKaHqP.cjs.map} +1 -1
- package/dist/sync-RsnjXYwG.cjs +4 -0
- package/dist/{types-eTlj5f2M.d.mts → types-wXMIMOyK.d.mts} +1 -1
- package/dist/{types-eTlj5f2M.d.mts.map → types-wXMIMOyK.d.mts.map} +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/package.json +3 -3
- package/src/dev/__tests__/index.spec.ts +49 -0
- package/src/dev/index.ts +84 -63
- package/src/index.ts +79 -1
- package/src/init/versions.ts +4 -4
- package/src/secrets/__tests__/reconcile.spec.ts +123 -0
- package/src/secrets/reconcile.ts +53 -0
- package/src/setup/fullstack-secrets.ts +2 -0
- package/dist/sync-BxFB34zW.cjs +0 -4
package/dist/index.mjs
CHANGED
|
@@ -8,8 +8,9 @@ import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecr
|
|
|
8
8
|
import { DokployApi } from "./dokploy-api-2ldYoN3i.mjs";
|
|
9
9
|
import { encryptSecrets } from "./encryption-BOH5M-f-.mjs";
|
|
10
10
|
import { CachedStateProvider } from "./CachedStateProvider-BDq5WqSy.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { createStageSecrets, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, rotateServicePassword, writeDockerEnvFromSecrets } from "./fullstack-secrets-UZAFWuH4.mjs";
|
|
12
|
+
import { generateReactQueryCommand } from "./openapi-react-query-C4UdILaI.mjs";
|
|
13
|
+
import { isSSMConfigured, pullSecrets, pushSecrets } from "./sync-CbeKrnQV.mjs";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
14
15
|
import { copyFileSync, existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
15
16
|
import { basename, dirname, join, parse, relative, resolve } from "node:path";
|
|
@@ -34,7 +35,7 @@ import prompts from "prompts";
|
|
|
34
35
|
|
|
35
36
|
//#region package.json
|
|
36
37
|
var name = "@geekmidas/cli";
|
|
37
|
-
var version = "1.
|
|
38
|
+
var version = "1.9.0";
|
|
38
39
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
39
40
|
var private$1 = false;
|
|
40
41
|
var type = "module";
|
|
@@ -1740,6 +1741,66 @@ var EntryRunner = class {
|
|
|
1740
1741
|
}
|
|
1741
1742
|
}
|
|
1742
1743
|
};
|
|
1744
|
+
/**
|
|
1745
|
+
* Generate the content of the dev server entry file (server.ts).
|
|
1746
|
+
* Uses dynamic import for createApp so Credentials are populated
|
|
1747
|
+
* before any app modules evaluate.
|
|
1748
|
+
* @internal Exported for testing
|
|
1749
|
+
*/
|
|
1750
|
+
function generateServerEntryContent(options) {
|
|
1751
|
+
const { secretsJsonPath, runtime = "node", enableOpenApi = false, appImportPath = "./app.js" } = options;
|
|
1752
|
+
const credentialsInjection = secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1753
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
1754
|
+
|
|
1755
|
+
// Inject dev secrets into Credentials (must happen before app import)
|
|
1756
|
+
const secretsPath = '${secretsJsonPath}';
|
|
1757
|
+
if (existsSync(secretsPath)) {
|
|
1758
|
+
Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
` : "";
|
|
1762
|
+
const serveCode = runtime === "bun" ? `Bun.serve({
|
|
1763
|
+
port,
|
|
1764
|
+
fetch: app.fetch,
|
|
1765
|
+
});` : `const { serve } = await import('@hono/node-server');
|
|
1766
|
+
const server = serve({
|
|
1767
|
+
fetch: app.fetch,
|
|
1768
|
+
port,
|
|
1769
|
+
});
|
|
1770
|
+
// Inject WebSocket support if available
|
|
1771
|
+
const injectWs = (app as any).__injectWebSocket;
|
|
1772
|
+
if (injectWs) {
|
|
1773
|
+
injectWs(server);
|
|
1774
|
+
console.log('🔌 Telescope real-time updates enabled');
|
|
1775
|
+
}`;
|
|
1776
|
+
return `#!/usr/bin/env node
|
|
1777
|
+
/**
|
|
1778
|
+
* Development server entry point
|
|
1779
|
+
* This file is auto-generated by 'gkm dev'
|
|
1780
|
+
*/
|
|
1781
|
+
${credentialsInjection}
|
|
1782
|
+
const port = process.argv.includes('--port')
|
|
1783
|
+
? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
|
|
1784
|
+
: 3000;
|
|
1785
|
+
|
|
1786
|
+
// Dynamic import so Credentials are populated before env.ts evaluates
|
|
1787
|
+
const { createApp } = await import('${appImportPath}');
|
|
1788
|
+
|
|
1789
|
+
// createApp is async to support optional WebSocket setup
|
|
1790
|
+
const { app, start } = await createApp(undefined, ${enableOpenApi});
|
|
1791
|
+
|
|
1792
|
+
// Start the server
|
|
1793
|
+
start({
|
|
1794
|
+
port,
|
|
1795
|
+
serve: async (app, port) => {
|
|
1796
|
+
${serveCode}
|
|
1797
|
+
},
|
|
1798
|
+
}).catch((error) => {
|
|
1799
|
+
console.error('Failed to start server:', error);
|
|
1800
|
+
process.exit(1);
|
|
1801
|
+
});
|
|
1802
|
+
`;
|
|
1803
|
+
}
|
|
1743
1804
|
var DevServer = class {
|
|
1744
1805
|
serverProcess = null;
|
|
1745
1806
|
isRunning = false;
|
|
@@ -1833,58 +1894,12 @@ var DevServer = class {
|
|
|
1833
1894
|
}
|
|
1834
1895
|
async createServerEntry() {
|
|
1835
1896
|
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1836
|
-
const { relative: relative$1, dirname: dirname$1 } = await import("node:path");
|
|
1837
1897
|
const serverPath = join(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1838
|
-
const
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const secretsPath = '${this.secretsJsonPath}';
|
|
1844
|
-
if (existsSync(secretsPath)) {
|
|
1845
|
-
Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
` : "";
|
|
1849
|
-
const serveCode = this.runtime === "bun" ? `Bun.serve({
|
|
1850
|
-
port,
|
|
1851
|
-
fetch: app.fetch,
|
|
1852
|
-
});` : `const { serve } = await import('@hono/node-server');
|
|
1853
|
-
const server = serve({
|
|
1854
|
-
fetch: app.fetch,
|
|
1855
|
-
port,
|
|
1856
|
-
});
|
|
1857
|
-
// Inject WebSocket support if available
|
|
1858
|
-
const injectWs = (app as any).__injectWebSocket;
|
|
1859
|
-
if (injectWs) {
|
|
1860
|
-
injectWs(server);
|
|
1861
|
-
console.log('🔌 Telescope real-time updates enabled');
|
|
1862
|
-
}`;
|
|
1863
|
-
const content = `#!/usr/bin/env node
|
|
1864
|
-
/**
|
|
1865
|
-
* Development server entry point
|
|
1866
|
-
* This file is auto-generated by 'gkm dev'
|
|
1867
|
-
*/
|
|
1868
|
-
${credentialsInjection}import { createApp } from './${relativeAppPath.startsWith(".") ? relativeAppPath : `./${relativeAppPath}`}';
|
|
1869
|
-
|
|
1870
|
-
const port = process.argv.includes('--port')
|
|
1871
|
-
? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
|
|
1872
|
-
: 3000;
|
|
1873
|
-
|
|
1874
|
-
// createApp is async to support optional WebSocket setup
|
|
1875
|
-
const { app, start } = await createApp(undefined, ${this.enableOpenApi});
|
|
1876
|
-
|
|
1877
|
-
// Start the server
|
|
1878
|
-
start({
|
|
1879
|
-
port,
|
|
1880
|
-
serve: async (app, port) => {
|
|
1881
|
-
${serveCode}
|
|
1882
|
-
},
|
|
1883
|
-
}).catch((error) => {
|
|
1884
|
-
console.error('Failed to start server:', error);
|
|
1885
|
-
process.exit(1);
|
|
1886
|
-
});
|
|
1887
|
-
`;
|
|
1898
|
+
const content = generateServerEntryContent({
|
|
1899
|
+
secretsJsonPath: this.secretsJsonPath,
|
|
1900
|
+
runtime: this.runtime,
|
|
1901
|
+
enableOpenApi: this.enableOpenApi
|
|
1902
|
+
});
|
|
1888
1903
|
await fsWriteFile(serverPath, content);
|
|
1889
1904
|
}
|
|
1890
1905
|
};
|
|
@@ -2137,7 +2152,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2137
2152
|
let masterKey;
|
|
2138
2153
|
if (context.production?.bundle && !skipBundle) {
|
|
2139
2154
|
logger$9.log(`\n📦 Bundling production server...`);
|
|
2140
|
-
const { bundleServer } = await import("./bundler-
|
|
2155
|
+
const { bundleServer } = await import("./bundler-DQYjKFPm.mjs");
|
|
2141
2156
|
const allConstructs = [
|
|
2142
2157
|
...endpoints.map((e) => e.construct),
|
|
2143
2158
|
...functions.map((f) => f.construct),
|
|
@@ -2416,11 +2431,11 @@ async function createDnsProvider(options) {
|
|
|
2416
2431
|
if (isDnsProvider(config$1.provider)) return config$1.provider;
|
|
2417
2432
|
const provider = config$1.provider;
|
|
2418
2433
|
if (provider === "hostinger") {
|
|
2419
|
-
const { HostingerProvider } = await import("./HostingerProvider-
|
|
2434
|
+
const { HostingerProvider } = await import("./HostingerProvider-ANWchdiK.mjs");
|
|
2420
2435
|
return new HostingerProvider();
|
|
2421
2436
|
}
|
|
2422
2437
|
if (provider === "route53") {
|
|
2423
|
-
const { Route53Provider } = await import("./Route53Provider-
|
|
2438
|
+
const { Route53Provider } = await import("./Route53Provider-QoPgcXxn.mjs");
|
|
2424
2439
|
const route53Config = config$1;
|
|
2425
2440
|
return new Route53Provider({
|
|
2426
2441
|
region: route53Config.region,
|
|
@@ -4668,19 +4683,19 @@ function isStateProvider(value) {
|
|
|
4668
4683
|
async function createStateProvider(options) {
|
|
4669
4684
|
const { config: config$1, workspaceRoot, workspaceName } = options;
|
|
4670
4685
|
if (!config$1) {
|
|
4671
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
4686
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
|
|
4672
4687
|
return new LocalStateProvider(workspaceRoot);
|
|
4673
4688
|
}
|
|
4674
4689
|
if (isStateProvider(config$1.provider)) return config$1.provider;
|
|
4675
4690
|
const provider = config$1.provider;
|
|
4676
4691
|
if (provider === "local") {
|
|
4677
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
4692
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
|
|
4678
4693
|
return new LocalStateProvider(workspaceRoot);
|
|
4679
4694
|
}
|
|
4680
4695
|
if (provider === "ssm") {
|
|
4681
4696
|
if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
|
|
4682
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
4683
|
-
const { SSMStateProvider } = await import("./SSMStateProvider-
|
|
4697
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
|
|
4698
|
+
const { SSMStateProvider } = await import("./SSMStateProvider-CksOTB8M.mjs");
|
|
4684
4699
|
const { CachedStateProvider: CachedStateProvider$1 } = await import("./CachedStateProvider-CI61keQ1.mjs");
|
|
4685
4700
|
const ssmConfig = config$1;
|
|
4686
4701
|
const local = new LocalStateProvider(workspaceRoot);
|
|
@@ -5780,7 +5795,7 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5780
5795
|
}
|
|
5781
5796
|
if (workspace.deploy?.backups && provisionedPostgres) {
|
|
5782
5797
|
logger$3.log("\n💾 Provisioning backup destination...");
|
|
5783
|
-
const { provisionBackupDestination } = await import("./backup-provisioner-
|
|
5798
|
+
const { provisionBackupDestination } = await import("./backup-provisioner-BEXoHTuC.mjs");
|
|
5784
5799
|
const backupState = await provisionBackupDestination({
|
|
5785
5800
|
api,
|
|
5786
5801
|
projectId: project.projectId,
|
|
@@ -6405,200 +6420,6 @@ function printStateDetails(state) {
|
|
|
6405
6420
|
}
|
|
6406
6421
|
}
|
|
6407
6422
|
|
|
6408
|
-
//#endregion
|
|
6409
|
-
//#region src/secrets/generator.ts
|
|
6410
|
-
/**
|
|
6411
|
-
* Generate a secure random password using URL-safe base64 characters.
|
|
6412
|
-
* @param length Password length (default: 32)
|
|
6413
|
-
*/
|
|
6414
|
-
function generateSecurePassword(length = 32) {
|
|
6415
|
-
return randomBytes(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
|
|
6416
|
-
}
|
|
6417
|
-
/** Default service configurations */
|
|
6418
|
-
const SERVICE_DEFAULTS = {
|
|
6419
|
-
postgres: {
|
|
6420
|
-
host: "postgres",
|
|
6421
|
-
port: 5432,
|
|
6422
|
-
username: "app",
|
|
6423
|
-
database: "app"
|
|
6424
|
-
},
|
|
6425
|
-
redis: {
|
|
6426
|
-
host: "redis",
|
|
6427
|
-
port: 6379,
|
|
6428
|
-
username: "default"
|
|
6429
|
-
},
|
|
6430
|
-
rabbitmq: {
|
|
6431
|
-
host: "rabbitmq",
|
|
6432
|
-
port: 5672,
|
|
6433
|
-
username: "app",
|
|
6434
|
-
vhost: "/"
|
|
6435
|
-
}
|
|
6436
|
-
};
|
|
6437
|
-
/**
|
|
6438
|
-
* Generate credentials for a specific service.
|
|
6439
|
-
*/
|
|
6440
|
-
function generateServiceCredentials(service) {
|
|
6441
|
-
const defaults = SERVICE_DEFAULTS[service];
|
|
6442
|
-
return {
|
|
6443
|
-
...defaults,
|
|
6444
|
-
password: generateSecurePassword()
|
|
6445
|
-
};
|
|
6446
|
-
}
|
|
6447
|
-
/**
|
|
6448
|
-
* Generate credentials for multiple services.
|
|
6449
|
-
*/
|
|
6450
|
-
function generateServicesCredentials(services) {
|
|
6451
|
-
const result = {};
|
|
6452
|
-
for (const service of services) result[service] = generateServiceCredentials(service);
|
|
6453
|
-
return result;
|
|
6454
|
-
}
|
|
6455
|
-
/**
|
|
6456
|
-
* Generate connection URL for PostgreSQL.
|
|
6457
|
-
*/
|
|
6458
|
-
function generatePostgresUrl(creds) {
|
|
6459
|
-
const { username, password, host, port, database } = creds;
|
|
6460
|
-
return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
6461
|
-
}
|
|
6462
|
-
/**
|
|
6463
|
-
* Generate connection URL for Redis.
|
|
6464
|
-
*/
|
|
6465
|
-
function generateRedisUrl(creds) {
|
|
6466
|
-
const { password, host, port } = creds;
|
|
6467
|
-
return `redis://:${encodeURIComponent(password)}@${host}:${port}`;
|
|
6468
|
-
}
|
|
6469
|
-
/**
|
|
6470
|
-
* Generate connection URL for RabbitMQ.
|
|
6471
|
-
*/
|
|
6472
|
-
function generateRabbitmqUrl(creds) {
|
|
6473
|
-
const { username, password, host, port, vhost } = creds;
|
|
6474
|
-
const encodedVhost = encodeURIComponent(vhost ?? "/");
|
|
6475
|
-
return `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;
|
|
6476
|
-
}
|
|
6477
|
-
/**
|
|
6478
|
-
* Generate connection URLs from service credentials.
|
|
6479
|
-
*/
|
|
6480
|
-
function generateConnectionUrls(services) {
|
|
6481
|
-
const urls = {};
|
|
6482
|
-
if (services.postgres) urls.DATABASE_URL = generatePostgresUrl(services.postgres);
|
|
6483
|
-
if (services.redis) urls.REDIS_URL = generateRedisUrl(services.redis);
|
|
6484
|
-
if (services.rabbitmq) urls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);
|
|
6485
|
-
return urls;
|
|
6486
|
-
}
|
|
6487
|
-
/**
|
|
6488
|
-
* Create a new StageSecrets object with generated credentials.
|
|
6489
|
-
*/
|
|
6490
|
-
function createStageSecrets(stage, services) {
|
|
6491
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6492
|
-
const serviceCredentials = generateServicesCredentials(services);
|
|
6493
|
-
const urls = generateConnectionUrls(serviceCredentials);
|
|
6494
|
-
return {
|
|
6495
|
-
stage,
|
|
6496
|
-
createdAt: now,
|
|
6497
|
-
updatedAt: now,
|
|
6498
|
-
services: serviceCredentials,
|
|
6499
|
-
urls,
|
|
6500
|
-
custom: {}
|
|
6501
|
-
};
|
|
6502
|
-
}
|
|
6503
|
-
/**
|
|
6504
|
-
* Rotate password for a specific service.
|
|
6505
|
-
*/
|
|
6506
|
-
function rotateServicePassword(secrets, service) {
|
|
6507
|
-
const currentCreds = secrets.services[service];
|
|
6508
|
-
if (!currentCreds) throw new Error(`Service "${service}" not configured in secrets`);
|
|
6509
|
-
const newCreds = {
|
|
6510
|
-
...currentCreds,
|
|
6511
|
-
password: generateSecurePassword()
|
|
6512
|
-
};
|
|
6513
|
-
const newServices = {
|
|
6514
|
-
...secrets.services,
|
|
6515
|
-
[service]: newCreds
|
|
6516
|
-
};
|
|
6517
|
-
return {
|
|
6518
|
-
...secrets,
|
|
6519
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6520
|
-
services: newServices,
|
|
6521
|
-
urls: generateConnectionUrls(newServices)
|
|
6522
|
-
};
|
|
6523
|
-
}
|
|
6524
|
-
|
|
6525
|
-
//#endregion
|
|
6526
|
-
//#region src/setup/fullstack-secrets.ts
|
|
6527
|
-
/**
|
|
6528
|
-
* Generate a secure random password for database users.
|
|
6529
|
-
* Uses a combination of timestamp and random bytes for uniqueness.
|
|
6530
|
-
*/
|
|
6531
|
-
function generateDbPassword() {
|
|
6532
|
-
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
|
|
6533
|
-
}
|
|
6534
|
-
/**
|
|
6535
|
-
* Generate database URL for an app.
|
|
6536
|
-
* All apps connect to the same database, but use different users/schemas.
|
|
6537
|
-
*/
|
|
6538
|
-
function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
|
|
6539
|
-
const userName = appName.replace(/-/g, "_");
|
|
6540
|
-
const dbName = `${projectName.replace(/-/g, "_")}_dev`;
|
|
6541
|
-
return `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;
|
|
6542
|
-
}
|
|
6543
|
-
/**
|
|
6544
|
-
* Generate fullstack-aware custom secrets for a workspace.
|
|
6545
|
-
*
|
|
6546
|
-
* Generates:
|
|
6547
|
-
* - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
|
|
6548
|
-
* - Per-app database passwords and URLs for backend apps with db service
|
|
6549
|
-
* - Better-auth secrets for apps using the better-auth framework
|
|
6550
|
-
*/
|
|
6551
|
-
function generateFullstackCustomSecrets(workspace) {
|
|
6552
|
-
const hasDb = !!workspace.services.db;
|
|
6553
|
-
const customs = {
|
|
6554
|
-
NODE_ENV: "development",
|
|
6555
|
-
PORT: "3000",
|
|
6556
|
-
LOG_LEVEL: "debug",
|
|
6557
|
-
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
6558
|
-
};
|
|
6559
|
-
if (!hasDb) return customs;
|
|
6560
|
-
const frontendPorts = [];
|
|
6561
|
-
for (const [appName, appConfig] of Object.entries(workspace.apps)) {
|
|
6562
|
-
if (appConfig.type === "frontend") {
|
|
6563
|
-
frontendPorts.push(appConfig.port);
|
|
6564
|
-
continue;
|
|
6565
|
-
}
|
|
6566
|
-
const password = generateDbPassword();
|
|
6567
|
-
const upperName = appName.toUpperCase();
|
|
6568
|
-
customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
|
|
6569
|
-
customs[`${upperName}_DB_PASSWORD`] = password;
|
|
6570
|
-
if (appConfig.framework === "better-auth") {
|
|
6571
|
-
customs.AUTH_PORT = String(appConfig.port);
|
|
6572
|
-
customs.AUTH_URL = `http://localhost:${appConfig.port}`;
|
|
6573
|
-
customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
|
|
6574
|
-
customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
|
|
6575
|
-
}
|
|
6576
|
-
}
|
|
6577
|
-
if (customs.BETTER_AUTH_SECRET) {
|
|
6578
|
-
const allPorts = Object.values(workspace.apps).map((a) => a.port);
|
|
6579
|
-
customs.BETTER_AUTH_TRUSTED_ORIGINS = allPorts.map((p) => `http://localhost:${p}`).join(",");
|
|
6580
|
-
}
|
|
6581
|
-
return customs;
|
|
6582
|
-
}
|
|
6583
|
-
/**
|
|
6584
|
-
* Extract *_DB_PASSWORD keys from secrets and write docker/.env.
|
|
6585
|
-
*
|
|
6586
|
-
* The docker/.env file contains database passwords that the PostgreSQL
|
|
6587
|
-
* init script reads to create per-app database users.
|
|
6588
|
-
*/
|
|
6589
|
-
async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
|
|
6590
|
-
const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
|
|
6591
|
-
if (dbPasswordEntries.length === 0) return;
|
|
6592
|
-
const envContent = `# Auto-generated docker environment file
|
|
6593
|
-
# Contains database passwords for docker-compose postgres init
|
|
6594
|
-
# This file is gitignored - do not commit to version control
|
|
6595
|
-
${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
|
|
6596
|
-
`;
|
|
6597
|
-
const envPath = join(workspaceRoot, "docker", ".env");
|
|
6598
|
-
await mkdir(dirname(envPath), { recursive: true });
|
|
6599
|
-
await writeFile(envPath, envContent);
|
|
6600
|
-
}
|
|
6601
|
-
|
|
6602
6423
|
//#endregion
|
|
6603
6424
|
//#region src/init/versions.ts
|
|
6604
6425
|
const require$1 = createRequire(import.meta.url);
|
|
@@ -6624,14 +6445,14 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6624
6445
|
"@geekmidas/audit": "~1.0.0",
|
|
6625
6446
|
"@geekmidas/auth": "~1.0.0",
|
|
6626
6447
|
"@geekmidas/cache": "~1.0.0",
|
|
6627
|
-
"@geekmidas/client": "~
|
|
6448
|
+
"@geekmidas/client": "~3.0.0",
|
|
6628
6449
|
"@geekmidas/cloud": "~1.0.0",
|
|
6629
|
-
"@geekmidas/constructs": "~
|
|
6450
|
+
"@geekmidas/constructs": "~2.0.0",
|
|
6630
6451
|
"@geekmidas/db": "~1.0.0",
|
|
6631
6452
|
"@geekmidas/emailkit": "~1.0.0",
|
|
6632
6453
|
"@geekmidas/envkit": "~1.0.3",
|
|
6633
6454
|
"@geekmidas/errors": "~1.0.0",
|
|
6634
|
-
"@geekmidas/events": "~1.
|
|
6455
|
+
"@geekmidas/events": "~1.1.0",
|
|
6635
6456
|
"@geekmidas/logger": "~1.0.0",
|
|
6636
6457
|
"@geekmidas/rate-limit": "~1.0.0",
|
|
6637
6458
|
"@geekmidas/schema": "~1.0.0",
|
|
@@ -6639,7 +6460,7 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6639
6460
|
"@geekmidas/storage": "~1.0.0",
|
|
6640
6461
|
"@geekmidas/studio": "~1.0.0",
|
|
6641
6462
|
"@geekmidas/telescope": "~1.0.0",
|
|
6642
|
-
"@geekmidas/testkit": "~1.0.
|
|
6463
|
+
"@geekmidas/testkit": "~1.0.2",
|
|
6643
6464
|
"@geekmidas/cli": CLI_VERSION
|
|
6644
6465
|
};
|
|
6645
6466
|
|
|
@@ -11943,8 +11764,19 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
11943
11764
|
const globalOptions = program.opts();
|
|
11944
11765
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11945
11766
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
11946
|
-
const { pushSecrets: pushSecrets$1 } = await import("./sync-
|
|
11767
|
+
const { pushSecrets: pushSecrets$1 } = await import("./sync-6FoT41G3.mjs");
|
|
11768
|
+
const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
|
|
11769
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
|
|
11947
11770
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11771
|
+
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
11772
|
+
if (secrets) {
|
|
11773
|
+
const result = reconcileMissingSecrets(secrets, workspace);
|
|
11774
|
+
if (result) {
|
|
11775
|
+
await writeStageSecrets$1(result.secrets, workspace.root);
|
|
11776
|
+
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
11777
|
+
for (const key of result.addedKeys) console.log(` + ${key}`);
|
|
11778
|
+
}
|
|
11779
|
+
}
|
|
11948
11780
|
await pushSecrets$1(options.stage, workspace);
|
|
11949
11781
|
console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
|
|
11950
11782
|
} catch (error) {
|
|
@@ -11957,14 +11789,21 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
11957
11789
|
const globalOptions = program.opts();
|
|
11958
11790
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11959
11791
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
11960
|
-
const { pullSecrets: pullSecrets$1 } = await import("./sync-
|
|
11792
|
+
const { pullSecrets: pullSecrets$1 } = await import("./sync-6FoT41G3.mjs");
|
|
11961
11793
|
const { writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
|
|
11794
|
+
const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
|
|
11962
11795
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11963
|
-
|
|
11796
|
+
let secrets = await pullSecrets$1(options.stage, workspace);
|
|
11964
11797
|
if (!secrets) {
|
|
11965
11798
|
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
11966
11799
|
process.exit(1);
|
|
11967
11800
|
}
|
|
11801
|
+
const result = reconcileMissingSecrets(secrets, workspace);
|
|
11802
|
+
if (result) {
|
|
11803
|
+
secrets = result.secrets;
|
|
11804
|
+
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
11805
|
+
for (const key of result.addedKeys) console.log(` + ${key}`);
|
|
11806
|
+
}
|
|
11968
11807
|
await writeStageSecrets$1(secrets, workspace.root);
|
|
11969
11808
|
console.log(`\n✓ Secrets pulled for stage "${options.stage}"`);
|
|
11970
11809
|
} catch (error) {
|
|
@@ -11972,6 +11811,32 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
11972
11811
|
process.exit(1);
|
|
11973
11812
|
}
|
|
11974
11813
|
});
|
|
11814
|
+
program.command("secrets:reconcile").description("Backfill missing custom secrets from workspace config").option("--stage <stage>", "Stage name", "development").action(async (options) => {
|
|
11815
|
+
try {
|
|
11816
|
+
const globalOptions = program.opts();
|
|
11817
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11818
|
+
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
11819
|
+
const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
|
|
11820
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
|
|
11821
|
+
const { workspace } = await loadWorkspaceConfig$1();
|
|
11822
|
+
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
11823
|
+
if (!secrets) {
|
|
11824
|
+
console.error(`No secrets found for stage "${options.stage}". Run "gkm secrets:init --stage ${options.stage}" first.`);
|
|
11825
|
+
process.exit(1);
|
|
11826
|
+
}
|
|
11827
|
+
const result = reconcileMissingSecrets(secrets, workspace);
|
|
11828
|
+
if (!result) {
|
|
11829
|
+
console.log(`\n✓ Secrets for stage "${options.stage}" are up-to-date`);
|
|
11830
|
+
return;
|
|
11831
|
+
}
|
|
11832
|
+
await writeStageSecrets$1(result.secrets, workspace.root);
|
|
11833
|
+
console.log(`\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage "${options.stage}":`);
|
|
11834
|
+
for (const key of result.addedKeys) console.log(` + ${key}`);
|
|
11835
|
+
} catch (error) {
|
|
11836
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
11837
|
+
process.exit(1);
|
|
11838
|
+
}
|
|
11839
|
+
});
|
|
11975
11840
|
program.command("deploy").description("Deploy application to a provider").requiredOption("--provider <provider>", "Deploy provider (docker, dokploy, aws-lambda)").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp)").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").action(async (options) => {
|
|
11976
11841
|
try {
|
|
11977
11842
|
const globalOptions = program.opts();
|