@geekmidas/cli 1.4.0 → 1.5.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 +12 -0
- package/dist/{HostingerProvider-B9N-TKbp.mjs → HostingerProvider-402UdK89.mjs} +34 -1
- package/dist/HostingerProvider-402UdK89.mjs.map +1 -0
- package/dist/{HostingerProvider-DUV9-Tzg.cjs → HostingerProvider-BiXdHjiq.cjs} +34 -1
- package/dist/HostingerProvider-BiXdHjiq.cjs.map +1 -0
- package/dist/{Route53Provider-DOWmFnwN.mjs → Route53Provider-DbBo7Uz5.mjs} +55 -2
- package/dist/Route53Provider-DbBo7Uz5.mjs.map +1 -0
- package/dist/{Route53Provider-xrWuBXih.cjs → Route53Provider-kfJ77LmL.cjs} +55 -2
- package/dist/Route53Provider-kfJ77LmL.cjs.map +1 -0
- package/dist/backup-provisioner-B5e-F6zX.cjs +164 -0
- package/dist/backup-provisioner-B5e-F6zX.cjs.map +1 -0
- package/dist/backup-provisioner-BIArpmTr.mjs +163 -0
- package/dist/backup-provisioner-BIArpmTr.mjs.map +1 -0
- package/dist/{config-C1dM7aZb.cjs → config-BYn5yUt5.cjs} +2 -2
- package/dist/{config-C1dM7aZb.cjs.map → config-BYn5yUt5.cjs.map} +1 -1
- package/dist/{config-C1bidhvG.mjs → config-dLNQIvDR.mjs} +2 -2
- package/dist/{config-C1bidhvG.mjs.map → config-dLNQIvDR.mjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/{dokploy-api-z0833e7r.mjs → dokploy-api-2ldYoN3i.mjs} +131 -1
- package/dist/dokploy-api-2ldYoN3i.mjs.map +1 -0
- package/dist/dokploy-api-C93pveuy.mjs +3 -0
- package/dist/dokploy-api-CbDh4o93.cjs +3 -0
- package/dist/{dokploy-api-CQvhV6Hd.cjs → dokploy-api-DLgvEQlr.cjs} +131 -1
- package/dist/dokploy-api-DLgvEQlr.cjs.map +1 -0
- package/dist/{index-DzmZ6SUW.d.cts → index-Ba21_lNt.d.cts} +157 -29
- package/dist/index-Ba21_lNt.d.cts.map +1 -0
- package/dist/{index-DvpWzLD7.d.mts → index-Bj5VNxEL.d.mts} +158 -30
- package/dist/index-Bj5VNxEL.d.mts.map +1 -0
- package/dist/index.cjs +219 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +219 -68
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-9k6a6VA4.mjs → openapi-CMTyaIJJ.mjs} +2 -2
- package/dist/{openapi-9k6a6VA4.mjs.map → openapi-CMTyaIJJ.mjs.map} +1 -1
- package/dist/{openapi-Dcja4e1C.cjs → openapi-CqblwJZ4.cjs} +2 -2
- package/dist/{openapi-Dcja4e1C.cjs.map → openapi-CqblwJZ4.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{types-B9UZ7fOG.d.mts → types-CZg5iUgD.d.mts} +1 -1
- package/dist/{types-B9UZ7fOG.d.mts.map → types-CZg5iUgD.d.mts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +1 -1
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-CeFgIDC-.cjs → workspace-DIMnYaYt.cjs} +20 -2
- package/dist/{workspace-CeFgIDC-.cjs.map → workspace-DIMnYaYt.cjs.map} +1 -1
- package/dist/{workspace-Cb_I7oCJ.mjs → workspace-Dy8k7Wru.mjs} +20 -2
- package/dist/{workspace-Cb_I7oCJ.mjs.map → workspace-Dy8k7Wru.mjs.map} +1 -1
- package/examples/cron-example.ts +6 -6
- package/examples/function-example.ts +1 -1
- package/package.json +7 -5
- package/src/deploy/__tests__/Route53Provider.spec.ts +23 -0
- package/src/deploy/__tests__/backup-provisioner.spec.ts +428 -0
- package/src/deploy/__tests__/createDnsProvider.spec.ts +23 -0
- package/src/deploy/__tests__/env-resolver.spec.ts +239 -0
- package/src/deploy/__tests__/sniffer.spec.ts +104 -93
- package/src/deploy/__tests__/undeploy.spec.ts +758 -0
- package/src/deploy/backup-provisioner.ts +316 -0
- package/src/deploy/dns/DnsProvider.ts +39 -1
- package/src/deploy/dns/HostingerProvider.ts +74 -0
- package/src/deploy/dns/Route53Provider.ts +85 -1
- package/src/deploy/dns/index.ts +25 -0
- package/src/deploy/dokploy-api.ts +237 -0
- package/src/deploy/env-resolver.ts +11 -1
- package/src/deploy/index.ts +143 -37
- package/src/deploy/sniffer.ts +39 -7
- package/src/deploy/state.ts +171 -0
- package/src/deploy/undeploy.ts +407 -0
- package/src/generators/FunctionGenerator.ts +1 -1
- package/src/init/generators/monorepo.ts +4 -0
- package/src/init/generators/web.ts +45 -2
- package/src/init/versions.ts +2 -2
- package/src/workspace/schema.ts +34 -0
- package/src/workspace/types.ts +37 -37
- package/dist/HostingerProvider-B9N-TKbp.mjs.map +0 -1
- package/dist/HostingerProvider-DUV9-Tzg.cjs.map +0 -1
- package/dist/Route53Provider-DOWmFnwN.mjs.map +0 -1
- package/dist/Route53Provider-xrWuBXih.cjs.map +0 -1
- package/dist/dokploy-api-CQvhV6Hd.cjs.map +0 -1
- package/dist/dokploy-api-CWc02yyg.cjs +0 -3
- package/dist/dokploy-api-DSJYNx88.mjs +0 -3
- package/dist/dokploy-api-z0833e7r.mjs.map +0 -1
- package/dist/index-DvpWzLD7.d.mts.map +0 -1
- package/dist/index-DzmZ6SUW.d.cts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
3
|
-
const require_workspace = require('./workspace-
|
|
4
|
-
const require_config = require('./config-
|
|
3
|
+
const require_workspace = require('./workspace-DIMnYaYt.cjs');
|
|
4
|
+
const require_config = require('./config-BYn5yUt5.cjs');
|
|
5
5
|
const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
6
|
-
const require_openapi = require('./openapi-
|
|
6
|
+
const require_openapi = require('./openapi-CqblwJZ4.cjs');
|
|
7
7
|
const require_storage = require('./storage-CoCNe0Pt.cjs');
|
|
8
|
-
const require_dokploy_api = require('./dokploy-api-
|
|
8
|
+
const require_dokploy_api = require('./dokploy-api-DLgvEQlr.cjs');
|
|
9
9
|
const require_encryption = require('./encryption-BE0UOb8j.cjs');
|
|
10
10
|
const require_CachedStateProvider = require('./CachedStateProvider-D73dCqfH.cjs');
|
|
11
11
|
const require_openapi_react_query = require('./openapi-react-query-BeXvk-wa.cjs');
|
|
@@ -32,7 +32,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
32
32
|
|
|
33
33
|
//#region package.json
|
|
34
34
|
var name = "@geekmidas/cli";
|
|
35
|
-
var version = "1.
|
|
35
|
+
var version = "1.5.0";
|
|
36
36
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
37
37
|
var private$1 = false;
|
|
38
38
|
var type = "module";
|
|
@@ -78,7 +78,9 @@ var repository = {
|
|
|
78
78
|
};
|
|
79
79
|
var dependencies = {
|
|
80
80
|
"@apidevtools/swagger-parser": "^10.1.0",
|
|
81
|
+
"@aws-sdk/client-iam": "~3.971.0",
|
|
81
82
|
"@aws-sdk/client-route-53": "~3.971.0",
|
|
83
|
+
"@aws-sdk/client-s3": "~3.971.0",
|
|
82
84
|
"@aws-sdk/client-ssm": "~3.971.0",
|
|
83
85
|
"@aws-sdk/credential-providers": "~3.971.0",
|
|
84
86
|
"@geekmidas/constructs": "workspace:~",
|
|
@@ -132,7 +134,7 @@ const logger$11 = console;
|
|
|
132
134
|
* Validate Dokploy token by making a test API call
|
|
133
135
|
*/
|
|
134
136
|
async function validateDokployToken(endpoint, token) {
|
|
135
|
-
const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-
|
|
137
|
+
const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-CbDh4o93.cjs"));
|
|
136
138
|
const api = new DokployApi$1({
|
|
137
139
|
baseUrl: endpoint,
|
|
138
140
|
token
|
|
@@ -417,7 +419,7 @@ var FunctionGenerator = class extends require_openapi.ConstructGenerator {
|
|
|
417
419
|
const importPath = relativePath.replace(/\.ts$/, ".js");
|
|
418
420
|
const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), context.envParserPath);
|
|
419
421
|
const relativeLoggerPath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), context.loggerPath);
|
|
420
|
-
const content = `import { AWSLambdaFunction } from '@geekmidas/constructs/
|
|
422
|
+
const content = `import { AWSLambdaFunction } from '@geekmidas/constructs/aws';
|
|
421
423
|
import { ${exportName} } from '${importPath}';
|
|
422
424
|
import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
|
|
423
425
|
import ${context.loggerImportPattern} from '${relativeLoggerPath}';
|
|
@@ -2176,6 +2178,41 @@ function isDnsVerified(state, hostname, serverIp) {
|
|
|
2176
2178
|
const record = state?.dnsVerified?.[hostname];
|
|
2177
2179
|
return record?.serverIp === serverIp;
|
|
2178
2180
|
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Get the key for a DNS record in state
|
|
2183
|
+
*/
|
|
2184
|
+
function getDnsRecordKey(name$1, type$1) {
|
|
2185
|
+
return `${name$1}:${type$1}`;
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Set a created DNS record in state (mutates state)
|
|
2189
|
+
*/
|
|
2190
|
+
function setDnsRecord(state, record) {
|
|
2191
|
+
if (!state.dnsRecords) state.dnsRecords = {};
|
|
2192
|
+
const key = getDnsRecordKey(record.name, record.type);
|
|
2193
|
+
state.dnsRecords[key] = {
|
|
2194
|
+
...record,
|
|
2195
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Get backup state from state
|
|
2200
|
+
*/
|
|
2201
|
+
function getBackupState(state) {
|
|
2202
|
+
return state?.backups;
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Set backup state (mutates state)
|
|
2206
|
+
*/
|
|
2207
|
+
function setBackupState(state, backupState) {
|
|
2208
|
+
state.backups = backupState;
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Set postgres backup ID in state (mutates state)
|
|
2212
|
+
*/
|
|
2213
|
+
function setPostgresBackupId(state, backupId) {
|
|
2214
|
+
if (state.backups) state.backups.postgresBackupId = backupId;
|
|
2215
|
+
}
|
|
2179
2216
|
|
|
2180
2217
|
//#endregion
|
|
2181
2218
|
//#region src/deploy/dns/DnsProvider.ts
|
|
@@ -2183,7 +2220,7 @@ function isDnsVerified(state, hostname, serverIp) {
|
|
|
2183
2220
|
* Check if value is a DnsProvider implementation.
|
|
2184
2221
|
*/
|
|
2185
2222
|
function isDnsProvider(value) {
|
|
2186
|
-
return typeof value === "object" && value !== null && typeof value.name === "string" && typeof value.getRecords === "function" && typeof value.upsertRecords === "function";
|
|
2223
|
+
return typeof value === "object" && value !== null && typeof value.name === "string" && typeof value.getRecords === "function" && typeof value.upsertRecords === "function" && typeof value.deleteRecords === "function";
|
|
2187
2224
|
}
|
|
2188
2225
|
/**
|
|
2189
2226
|
* Create a DNS provider based on configuration.
|
|
@@ -2199,11 +2236,11 @@ async function createDnsProvider(options) {
|
|
|
2199
2236
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
2200
2237
|
const provider = config.provider;
|
|
2201
2238
|
if (provider === "hostinger") {
|
|
2202
|
-
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-
|
|
2239
|
+
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-BiXdHjiq.cjs"));
|
|
2203
2240
|
return new HostingerProvider();
|
|
2204
2241
|
}
|
|
2205
2242
|
if (provider === "route53") {
|
|
2206
|
-
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-
|
|
2243
|
+
const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-kfJ77LmL.cjs"));
|
|
2207
2244
|
const route53Config = config;
|
|
2208
2245
|
return new Route53Provider({
|
|
2209
2246
|
region: route53Config.region,
|
|
@@ -2414,8 +2451,13 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
2414
2451
|
* Supports both legacy single-domain format and new multi-domain format:
|
|
2415
2452
|
* - Legacy: { provider: 'hostinger', domain: 'example.com' }
|
|
2416
2453
|
* - Multi: { 'example.com': { provider: 'hostinger' }, 'example.dev': { provider: 'route53' } }
|
|
2454
|
+
*
|
|
2455
|
+
* @param appHostnames - Map of app names to hostnames
|
|
2456
|
+
* @param dnsConfig - DNS configuration (legacy or multi-domain)
|
|
2457
|
+
* @param dokployEndpoint - Dokploy server endpoint to resolve IP from
|
|
2458
|
+
* @param state - Optional state to save created records for later deletion
|
|
2417
2459
|
*/
|
|
2418
|
-
async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint) {
|
|
2460
|
+
async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
2419
2461
|
if (!dnsConfig) return null;
|
|
2420
2462
|
const normalizedConfig = normalizeDnsConfig(dnsConfig);
|
|
2421
2463
|
logger$6.log("\n🌐 Setting up DNS records...");
|
|
@@ -2461,6 +2503,15 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint) {
|
|
|
2461
2503
|
logger$6.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
|
|
2462
2504
|
hasFailures = true;
|
|
2463
2505
|
}
|
|
2506
|
+
if (state) {
|
|
2507
|
+
for (const record of domainRecords) if (record.created || record.existed) setDnsRecord(state, {
|
|
2508
|
+
domain: rootDomain,
|
|
2509
|
+
name: record.subdomain,
|
|
2510
|
+
type: record.type,
|
|
2511
|
+
value: record.value,
|
|
2512
|
+
ttl: "ttl" in providerConfig && providerConfig.ttl ? providerConfig.ttl : 300
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2464
2515
|
printDnsRecordsTable(domainRecords, rootDomain);
|
|
2465
2516
|
if (providerConfig.provider === "manual" || failed > 0) printDnsRecordsSimple(domainRecords.filter((r) => !r.created && !r.existed), rootDomain);
|
|
2466
2517
|
}
|
|
@@ -4137,33 +4188,6 @@ function isMainFrontendApp(appName, app, allApps) {
|
|
|
4137
4188
|
for (const [name$1, a] of Object.entries(allApps)) if (a.type === "frontend") return name$1 === appName;
|
|
4138
4189
|
return false;
|
|
4139
4190
|
}
|
|
4140
|
-
/**
|
|
4141
|
-
* Generate public URL build args for a frontend app based on its dependencies.
|
|
4142
|
-
*
|
|
4143
|
-
* @param app - The frontend app configuration
|
|
4144
|
-
* @param deployedUrls - Map of app name to deployed public URL
|
|
4145
|
-
* @returns Array of build args like 'NEXT_PUBLIC_API_URL=https://api.example.com'
|
|
4146
|
-
*/
|
|
4147
|
-
function generatePublicUrlBuildArgs(app, deployedUrls) {
|
|
4148
|
-
const buildArgs = [];
|
|
4149
|
-
for (const dep of app.dependencies) {
|
|
4150
|
-
const publicUrl = deployedUrls[dep];
|
|
4151
|
-
if (publicUrl) {
|
|
4152
|
-
const envVarName = `NEXT_PUBLIC_${dep.toUpperCase()}_URL`;
|
|
4153
|
-
buildArgs.push(`${envVarName}=${publicUrl}`);
|
|
4154
|
-
}
|
|
4155
|
-
}
|
|
4156
|
-
return buildArgs;
|
|
4157
|
-
}
|
|
4158
|
-
/**
|
|
4159
|
-
* Get public URL arg names from app dependencies.
|
|
4160
|
-
*
|
|
4161
|
-
* @param app - The frontend app configuration
|
|
4162
|
-
* @returns Array of arg names like 'NEXT_PUBLIC_API_URL'
|
|
4163
|
-
*/
|
|
4164
|
-
function getPublicUrlArgNames(app) {
|
|
4165
|
-
return app.dependencies.map((dep) => `NEXT_PUBLIC_${dep.toUpperCase()}_URL`);
|
|
4166
|
-
}
|
|
4167
4191
|
|
|
4168
4192
|
//#endregion
|
|
4169
4193
|
//#region src/deploy/env-resolver.ts
|
|
@@ -4225,7 +4249,9 @@ function resolveEnvVar(varName, context) {
|
|
|
4225
4249
|
break;
|
|
4226
4250
|
}
|
|
4227
4251
|
if (context.dependencyUrls && varName.endsWith("_URL")) {
|
|
4228
|
-
|
|
4252
|
+
let depName;
|
|
4253
|
+
if (varName.startsWith("NEXT_PUBLIC_")) depName = varName.slice(12, -4).toLowerCase();
|
|
4254
|
+
else depName = varName.slice(0, -4).toLowerCase();
|
|
4229
4255
|
if (context.dependencyUrls[depName]) return context.dependencyUrls[depName];
|
|
4230
4256
|
}
|
|
4231
4257
|
if (context.userSecrets) {
|
|
@@ -4637,14 +4663,29 @@ function resolveSnifferFile(baseName) {
|
|
|
4637
4663
|
*/
|
|
4638
4664
|
async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
|
|
4639
4665
|
const { logWarnings = true } = options;
|
|
4640
|
-
if (app.type === "frontend")
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4666
|
+
if (app.type === "frontend") {
|
|
4667
|
+
const depVars = (app.dependencies ?? []).map((dep) => `NEXT_PUBLIC_${dep.toUpperCase()}_URL`);
|
|
4668
|
+
if (app.config) {
|
|
4669
|
+
const sniffedVars = [];
|
|
4670
|
+
const configPaths = [];
|
|
4671
|
+
if (app.config.client) configPaths.push(app.config.client);
|
|
4672
|
+
if (app.config.server) configPaths.push(app.config.server);
|
|
4673
|
+
for (const configPath of configPaths) {
|
|
4674
|
+
const result = await sniffEntryFile(configPath, app.path, workspacePath);
|
|
4675
|
+
if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Config file "${configPath}" threw error during sniffing (env vars still captured): ${result.error.message}`);
|
|
4676
|
+
sniffedVars.push(...result.envVars);
|
|
4677
|
+
}
|
|
4678
|
+
const allVars = [...new Set([...depVars, ...sniffedVars])];
|
|
4679
|
+
return {
|
|
4680
|
+
appName,
|
|
4681
|
+
requiredEnvVars: allVars
|
|
4682
|
+
};
|
|
4683
|
+
}
|
|
4684
|
+
return {
|
|
4685
|
+
appName,
|
|
4686
|
+
requiredEnvVars: depVars
|
|
4687
|
+
};
|
|
4688
|
+
}
|
|
4648
4689
|
if (app.entry) {
|
|
4649
4690
|
const result = await sniffEntryFile(app.entry, app.path, workspacePath);
|
|
4650
4691
|
if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Entry file threw error during sniffing (env vars still captured): ${result.error.message}`);
|
|
@@ -5018,27 +5059,40 @@ async function initializePostgresUsers(api, postgres, serverHostname, users) {
|
|
|
5018
5059
|
for (const user of users) {
|
|
5019
5060
|
const schemaName = user.usePublicSchema ? "public" : user.name;
|
|
5020
5061
|
logger$1.log(` Creating user "${user.name}" with schema "${schemaName}"...`);
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5062
|
+
if (user.usePublicSchema) {
|
|
5063
|
+
await client.query(`
|
|
5064
|
+
DO $$ BEGIN
|
|
5065
|
+
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${user.name}') THEN
|
|
5066
|
+
CREATE USER "${user.name}" WITH PASSWORD '${user.password}';
|
|
5067
|
+
ELSE
|
|
5068
|
+
ALTER USER "${user.name}" WITH PASSWORD '${user.password}';
|
|
5069
|
+
END IF;
|
|
5070
|
+
END $$;
|
|
5071
|
+
`);
|
|
5072
|
+
await client.query(`
|
|
5031
5073
|
GRANT ALL ON SCHEMA public TO "${user.name}";
|
|
5032
5074
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${user.name}";
|
|
5033
5075
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "${user.name}";
|
|
5034
5076
|
`);
|
|
5035
|
-
else
|
|
5077
|
+
} else {
|
|
5078
|
+
await client.query(`
|
|
5079
|
+
DO $$ BEGIN
|
|
5080
|
+
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${user.name}') THEN
|
|
5081
|
+
CREATE USER "${user.name}" WITH PASSWORD '${user.password}';
|
|
5082
|
+
ELSE
|
|
5083
|
+
ALTER USER "${user.name}" WITH PASSWORD '${user.password}';
|
|
5084
|
+
END IF;
|
|
5085
|
+
-- Set search_path in same transaction to avoid tuple conflict
|
|
5086
|
+
ALTER USER "${user.name}" SET search_path TO "${schemaName}";
|
|
5087
|
+
END $$;
|
|
5088
|
+
`);
|
|
5089
|
+
await client.query(`
|
|
5036
5090
|
CREATE SCHEMA IF NOT EXISTS "${schemaName}" AUTHORIZATION "${user.name}";
|
|
5037
|
-
ALTER USER "${user.name}" SET search_path TO "${schemaName}";
|
|
5038
5091
|
GRANT USAGE ON SCHEMA "${schemaName}" TO "${user.name}";
|
|
5039
5092
|
GRANT ALL ON ALL TABLES IN SCHEMA "${schemaName}" TO "${user.name}";
|
|
5040
5093
|
ALTER DEFAULT PRIVILEGES IN SCHEMA "${schemaName}" GRANT ALL ON TABLES TO "${user.name}";
|
|
5041
5094
|
`);
|
|
5095
|
+
}
|
|
5042
5096
|
logger$1.log(` ✓ User "${user.name}" configured`);
|
|
5043
5097
|
}
|
|
5044
5098
|
} finally {
|
|
@@ -5544,6 +5598,36 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5544
5598
|
await initializePostgresUsers(api, provisionedPostgres, serverHostname, usersToCreate);
|
|
5545
5599
|
}
|
|
5546
5600
|
}
|
|
5601
|
+
if (workspace.deploy?.backups && provisionedPostgres) {
|
|
5602
|
+
logger$1.log("\n💾 Provisioning backup destination...");
|
|
5603
|
+
const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-B5e-F6zX.cjs"));
|
|
5604
|
+
const backupState = await provisionBackupDestination({
|
|
5605
|
+
api,
|
|
5606
|
+
projectId: project.projectId,
|
|
5607
|
+
projectName: workspace.name,
|
|
5608
|
+
stage,
|
|
5609
|
+
config: workspace.deploy.backups,
|
|
5610
|
+
existingState: getBackupState(state),
|
|
5611
|
+
logger: logger$1
|
|
5612
|
+
});
|
|
5613
|
+
setBackupState(state, backupState);
|
|
5614
|
+
if (!backupState.postgresBackupId) {
|
|
5615
|
+
const backupSchedule = workspace.deploy.backups.schedule ?? "0 2 * * *";
|
|
5616
|
+
const backupRetention = workspace.deploy.backups.retention ?? 30;
|
|
5617
|
+
logger$1.log(" Creating postgres backup schedule...");
|
|
5618
|
+
const backup = await api.createPostgresBackup({
|
|
5619
|
+
schedule: backupSchedule,
|
|
5620
|
+
prefix: `${stage}/postgres`,
|
|
5621
|
+
destinationId: backupState.destinationId,
|
|
5622
|
+
database: provisionedPostgres.databaseName,
|
|
5623
|
+
postgresId: provisionedPostgres.postgresId,
|
|
5624
|
+
enabled: true,
|
|
5625
|
+
keepLatestCount: backupRetention
|
|
5626
|
+
});
|
|
5627
|
+
setPostgresBackupId(state, backup.backupId);
|
|
5628
|
+
logger$1.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
|
|
5629
|
+
} else logger$1.log(" ✓ Using existing postgres backup schedule");
|
|
5630
|
+
}
|
|
5547
5631
|
const publicUrls = {};
|
|
5548
5632
|
const results = [];
|
|
5549
5633
|
const dokployConfig = workspace.deploy.dokploy;
|
|
@@ -5710,8 +5794,33 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5710
5794
|
else logger$1.log(` Found existing application: ${application.applicationId}`);
|
|
5711
5795
|
}
|
|
5712
5796
|
setApplicationId(state, appName, application.applicationId);
|
|
5713
|
-
const
|
|
5714
|
-
if (
|
|
5797
|
+
const dependencyUrls = {};
|
|
5798
|
+
if (app.dependencies) {
|
|
5799
|
+
for (const dep of app.dependencies) if (publicUrls[dep]) dependencyUrls[dep] = publicUrls[dep];
|
|
5800
|
+
}
|
|
5801
|
+
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
5802
|
+
const frontendHost = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
|
|
5803
|
+
const envContext = {
|
|
5804
|
+
app,
|
|
5805
|
+
appName,
|
|
5806
|
+
stage,
|
|
5807
|
+
state,
|
|
5808
|
+
appHostname: frontendHost,
|
|
5809
|
+
frontendUrls: [],
|
|
5810
|
+
userSecrets: stageSecrets ?? void 0,
|
|
5811
|
+
dependencyUrls
|
|
5812
|
+
};
|
|
5813
|
+
const sniffedVars = sniffedApps.get(appName)?.requiredEnvVars ?? [];
|
|
5814
|
+
const { valid, missing, resolved } = validateEnvVars(sniffedVars, envContext);
|
|
5815
|
+
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
5816
|
+
if (Object.keys(resolved).length > 0) logger$1.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
|
|
5817
|
+
const buildArgs = [];
|
|
5818
|
+
const publicUrlArgNames = [];
|
|
5819
|
+
for (const [key, value] of Object.entries(resolved)) if (key.startsWith("NEXT_PUBLIC_")) {
|
|
5820
|
+
buildArgs.push(`${key}=${value}`);
|
|
5821
|
+
publicUrlArgNames.push(key);
|
|
5822
|
+
}
|
|
5823
|
+
if (buildArgs.length > 0) logger$1.log(` Build args: ${publicUrlArgNames.join(", ")}`);
|
|
5715
5824
|
const imageName = `${workspace.name}-${appName}`;
|
|
5716
5825
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
5717
5826
|
logger$1.log(` Building Docker image: ${imageRef}`);
|
|
@@ -5725,19 +5834,18 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5725
5834
|
appName
|
|
5726
5835
|
},
|
|
5727
5836
|
buildArgs,
|
|
5728
|
-
publicUrlArgs:
|
|
5837
|
+
publicUrlArgs: publicUrlArgNames
|
|
5729
5838
|
});
|
|
5730
5839
|
const envVars = [
|
|
5731
5840
|
`NODE_ENV=production`,
|
|
5732
5841
|
`PORT=${app.port}`,
|
|
5733
5842
|
`STAGE=${stage}`
|
|
5734
5843
|
];
|
|
5844
|
+
for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
|
|
5735
5845
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
5736
5846
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
5737
5847
|
logger$1.log(` Deploying to Dokploy...`);
|
|
5738
5848
|
await api.deployApplication(application.applicationId);
|
|
5739
|
-
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
5740
|
-
const frontendHost = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
|
|
5741
5849
|
const existingFrontendDomains = await api.getDomainsByApplicationId(application.applicationId);
|
|
5742
5850
|
const existingFrontendDomain = existingFrontendDomains.find((d) => d.host === frontendHost);
|
|
5743
5851
|
if (existingFrontendDomain) {
|
|
@@ -6261,10 +6369,10 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6261
6369
|
"@geekmidas/cache": "~1.0.0",
|
|
6262
6370
|
"@geekmidas/client": "~1.0.0",
|
|
6263
6371
|
"@geekmidas/cloud": "~1.0.0",
|
|
6264
|
-
"@geekmidas/constructs": "~1.0.
|
|
6372
|
+
"@geekmidas/constructs": "~1.0.4",
|
|
6265
6373
|
"@geekmidas/db": "~1.0.0",
|
|
6266
6374
|
"@geekmidas/emailkit": "~1.0.0",
|
|
6267
|
-
"@geekmidas/envkit": "~1.0.
|
|
6375
|
+
"@geekmidas/envkit": "~1.0.1",
|
|
6268
6376
|
"@geekmidas/errors": "~1.0.0",
|
|
6269
6377
|
"@geekmidas/events": "~1.0.0",
|
|
6270
6378
|
"@geekmidas/logger": "~1.0.0",
|
|
@@ -7365,6 +7473,10 @@ export default defineWorkspace({
|
|
|
7365
7473
|
path: 'apps/web',
|
|
7366
7474
|
port: 3001,
|
|
7367
7475
|
dependencies: ['api', 'auth'],
|
|
7476
|
+
config: {
|
|
7477
|
+
client: './src/config/client.ts',
|
|
7478
|
+
server: './src/config/server.ts',
|
|
7479
|
+
},
|
|
7368
7480
|
client: {
|
|
7369
7481
|
output: './src/api',
|
|
7370
7482
|
},
|
|
@@ -9959,12 +10071,42 @@ export function getQueryClient() {
|
|
|
9959
10071
|
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
|
9960
10072
|
return browserQueryClient;
|
|
9961
10073
|
}
|
|
10074
|
+
`;
|
|
10075
|
+
const clientConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';
|
|
10076
|
+
|
|
10077
|
+
// Client config - only NEXT_PUBLIC_* vars (available in browser)
|
|
10078
|
+
// These values are inlined at build time by Next.js
|
|
10079
|
+
const envParser = new EnvironmentParser({
|
|
10080
|
+
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
|
|
10081
|
+
NEXT_PUBLIC_AUTH_URL: process.env.NEXT_PUBLIC_AUTH_URL,
|
|
10082
|
+
});
|
|
10083
|
+
|
|
10084
|
+
export const clientConfig = envParser
|
|
10085
|
+
.create((get) => ({
|
|
10086
|
+
apiUrl: get('NEXT_PUBLIC_API_URL').string(),
|
|
10087
|
+
authUrl: get('NEXT_PUBLIC_AUTH_URL').string(),
|
|
10088
|
+
}))
|
|
10089
|
+
.parse();
|
|
10090
|
+
`;
|
|
10091
|
+
const serverConfigTs = `import { EnvironmentParser } from '@geekmidas/envkit';
|
|
10092
|
+
|
|
10093
|
+
// Server config - all env vars (server-side only, not exposed to browser)
|
|
10094
|
+
// Access these only in Server Components, Route Handlers, or Server Actions
|
|
10095
|
+
const envParser = new EnvironmentParser({ ...process.env });
|
|
10096
|
+
|
|
10097
|
+
export const serverConfig = envParser
|
|
10098
|
+
.create((get) => ({
|
|
10099
|
+
// Add server-only secrets here
|
|
10100
|
+
// Example: stripeSecretKey: get('STRIPE_SECRET_KEY').string(),
|
|
10101
|
+
}))
|
|
10102
|
+
.parse();
|
|
9962
10103
|
`;
|
|
9963
10104
|
const authClientTs = `import { createAuthClient } from 'better-auth/react';
|
|
9964
10105
|
import { magicLinkClient } from 'better-auth/client/plugins';
|
|
10106
|
+
import { clientConfig } from '~/config/client';
|
|
9965
10107
|
|
|
9966
10108
|
export const authClient = createAuthClient({
|
|
9967
|
-
baseURL:
|
|
10109
|
+
baseURL: clientConfig.authUrl,
|
|
9968
10110
|
plugins: [magicLinkClient()],
|
|
9969
10111
|
});
|
|
9970
10112
|
|
|
@@ -9985,9 +10127,10 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
9985
10127
|
`;
|
|
9986
10128
|
const apiIndexTs = `import { createApi } from './openapi';
|
|
9987
10129
|
import { getQueryClient } from '~/lib/query-client';
|
|
10130
|
+
import { clientConfig } from '~/config/client';
|
|
9988
10131
|
|
|
9989
10132
|
export const api = createApi({
|
|
9990
|
-
baseURL:
|
|
10133
|
+
baseURL: clientConfig.apiUrl,
|
|
9991
10134
|
queryClient: getQueryClient(),
|
|
9992
10135
|
});
|
|
9993
10136
|
`;
|
|
@@ -10108,6 +10251,14 @@ node_modules/
|
|
|
10108
10251
|
path: "apps/web/src/app/page.tsx",
|
|
10109
10252
|
content: pageTsx
|
|
10110
10253
|
},
|
|
10254
|
+
{
|
|
10255
|
+
path: "apps/web/src/config/client.ts",
|
|
10256
|
+
content: clientConfigTs
|
|
10257
|
+
},
|
|
10258
|
+
{
|
|
10259
|
+
path: "apps/web/src/config/server.ts",
|
|
10260
|
+
content: serverConfigTs
|
|
10261
|
+
},
|
|
10111
10262
|
{
|
|
10112
10263
|
path: "apps/web/src/lib/query-client.ts",
|
|
10113
10264
|
content: queryClientTs
|