@geekmidas/cli 0.38.0 → 0.40.0
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/dist/{bundler-DQIuE3Kn.mjs → bundler-Db83tLti.mjs} +2 -2
- package/dist/{bundler-DQIuE3Kn.mjs.map → bundler-Db83tLti.mjs.map} +1 -1
- package/dist/{bundler-CyHg1v_T.cjs → bundler-DsXfFSCU.cjs} +2 -2
- package/dist/{bundler-CyHg1v_T.cjs.map → bundler-DsXfFSCU.cjs.map} +1 -1
- package/dist/{config-BC5n1a2D.mjs → config-C0b0jdmU.mjs} +2 -2
- package/dist/{config-BC5n1a2D.mjs.map → config-C0b0jdmU.mjs.map} +1 -1
- package/dist/{config-BAE9LFC1.cjs → config-xVZsRjN7.cjs} +2 -2
- package/dist/{config-BAE9LFC1.cjs.map → config-xVZsRjN7.cjs.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-Bdmk5ImW.cjs +3 -0
- package/dist/{dokploy-api-C5czOZoc.cjs → dokploy-api-BdxOMH_V.cjs} +43 -1
- package/dist/{dokploy-api-C5czOZoc.cjs.map → dokploy-api-BdxOMH_V.cjs.map} +1 -1
- package/dist/{dokploy-api-B9qR2Yn1.mjs → dokploy-api-DWsqNjwP.mjs} +43 -1
- package/dist/{dokploy-api-B9qR2Yn1.mjs.map → dokploy-api-DWsqNjwP.mjs.map} +1 -1
- package/dist/dokploy-api-tZSZaHd9.mjs +3 -0
- package/dist/{encryption-JtMsiGNp.mjs → encryption-BC4MAODn.mjs} +1 -1
- package/dist/{encryption-JtMsiGNp.mjs.map → encryption-BC4MAODn.mjs.map} +1 -1
- package/dist/encryption-Biq0EZ4m.cjs +4 -0
- package/dist/encryption-CQXBZGkt.mjs +3 -0
- package/dist/{encryption-BAz0xQ1Q.cjs → encryption-DaCB_NmS.cjs} +13 -3
- package/dist/{encryption-BAz0xQ1Q.cjs.map → encryption-DaCB_NmS.cjs.map} +1 -1
- package/dist/{index-C7TkoYmt.d.mts → index-CXa3odEw.d.mts} +68 -7
- package/dist/index-CXa3odEw.d.mts.map +1 -0
- package/dist/{index-CpchsC9w.d.cts → index-E8Nu2Rxl.d.cts} +67 -6
- package/dist/index-E8Nu2Rxl.d.cts.map +1 -0
- package/dist/index.cjs +787 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +767 -125
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CjYeF-Tg.mjs → openapi-D3pA6FfZ.mjs} +2 -2
- package/dist/{openapi-CjYeF-Tg.mjs.map → openapi-D3pA6FfZ.mjs.map} +1 -1
- package/dist/{openapi-a-e3Y8WA.cjs → openapi-DhcCtKzM.cjs} +2 -2
- package/dist/{openapi-a-e3Y8WA.cjs.map → openapi-DhcCtKzM.cjs.map} +1 -1
- package/dist/{openapi-react-query-DvNpdDpM.cjs → openapi-react-query-C_MxpBgF.cjs} +1 -1
- package/dist/{openapi-react-query-DvNpdDpM.cjs.map → openapi-react-query-C_MxpBgF.cjs.map} +1 -1
- package/dist/{openapi-react-query-5rSortLH.mjs → openapi-react-query-ZoP9DPbY.mjs} +1 -1
- package/dist/{openapi-react-query-5rSortLH.mjs.map → openapi-react-query-ZoP9DPbY.mjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{types-K2uQJ-FO.d.mts → types-BtGL-8QS.d.mts} +1 -1
- package/dist/{types-K2uQJ-FO.d.mts.map → types-BtGL-8QS.d.mts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-My0A4IRO.cjs → workspace-BDAhr6Kb.cjs} +33 -4
- package/dist/{workspace-My0A4IRO.cjs.map → workspace-BDAhr6Kb.cjs.map} +1 -1
- package/dist/{workspace-DFJ3sWfY.mjs → workspace-D_6ZCaR_.mjs} +33 -4
- package/dist/{workspace-DFJ3sWfY.mjs.map → workspace-D_6ZCaR_.mjs.map} +1 -1
- package/package.json +5 -5
- package/src/build/index.ts +23 -6
- package/src/deploy/__tests__/domain.spec.ts +231 -0
- package/src/deploy/__tests__/secrets.spec.ts +300 -0
- package/src/deploy/__tests__/sniffer.spec.ts +221 -0
- package/src/deploy/docker.ts +58 -29
- package/src/deploy/dokploy-api.ts +99 -0
- package/src/deploy/domain.ts +125 -0
- package/src/deploy/index.ts +364 -145
- package/src/deploy/secrets.ts +182 -0
- package/src/deploy/sniffer.ts +180 -0
- package/src/dev/index.ts +155 -9
- package/src/docker/index.ts +17 -2
- package/src/docker/templates.ts +171 -1
- package/src/index.ts +18 -1
- package/src/init/generators/auth.ts +2 -0
- package/src/init/versions.ts +2 -2
- package/src/workspace/index.ts +2 -0
- package/src/workspace/schema.ts +32 -6
- package/src/workspace/types.ts +64 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/dokploy-api-B0w17y4_.mjs +0 -3
- package/dist/dokploy-api-BnGeUqN4.cjs +0 -3
- package/dist/index-C7TkoYmt.d.mts.map +0 -1
- package/dist/index-CpchsC9w.d.cts.map +0 -1
package/src/docker/templates.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface FrontendDockerfileOptions {
|
|
|
25
25
|
turboPackage: string;
|
|
26
26
|
/** Detected package manager */
|
|
27
27
|
packageManager: PackageManager;
|
|
28
|
+
/**
|
|
29
|
+
* Public URL build args to include in the Dockerfile.
|
|
30
|
+
* These will be declared as ARG and converted to ENV for Next.js build.
|
|
31
|
+
* Example: ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL']
|
|
32
|
+
*/
|
|
33
|
+
publicUrlArgs?: string[];
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export interface MultiStageDockerfileOptions extends DockerTemplateOptions {
|
|
@@ -568,7 +574,14 @@ export function resolveDockerConfig(
|
|
|
568
574
|
export function generateNextjsDockerfile(
|
|
569
575
|
options: FrontendDockerfileOptions,
|
|
570
576
|
): string {
|
|
571
|
-
const {
|
|
577
|
+
const {
|
|
578
|
+
baseImage,
|
|
579
|
+
port,
|
|
580
|
+
appPath,
|
|
581
|
+
turboPackage,
|
|
582
|
+
packageManager,
|
|
583
|
+
publicUrlArgs = ['NEXT_PUBLIC_API_URL', 'NEXT_PUBLIC_AUTH_URL'],
|
|
584
|
+
} = options;
|
|
572
585
|
|
|
573
586
|
const pm = getPmConfig(packageManager);
|
|
574
587
|
const installPm = pm.install ? `RUN ${pm.install}` : '';
|
|
@@ -580,6 +593,14 @@ export function generateNextjsDockerfile(
|
|
|
580
593
|
// Use pnpm dlx for pnpm (avoids global bin dir issues in Docker)
|
|
581
594
|
const turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';
|
|
582
595
|
|
|
596
|
+
// Generate ARG and ENV declarations for public URLs
|
|
597
|
+
const publicUrlArgDeclarations = publicUrlArgs
|
|
598
|
+
.map((arg) => `ARG ${arg}=""`)
|
|
599
|
+
.join('\n');
|
|
600
|
+
const publicUrlEnvDeclarations = publicUrlArgs
|
|
601
|
+
.map((arg) => `ENV ${arg}=$${arg}`)
|
|
602
|
+
.join('\n');
|
|
603
|
+
|
|
583
604
|
return `# syntax=docker/dockerfile:1
|
|
584
605
|
# Next.js standalone Dockerfile with turbo prune optimization
|
|
585
606
|
|
|
@@ -615,6 +636,13 @@ FROM deps AS builder
|
|
|
615
636
|
|
|
616
637
|
WORKDIR /app
|
|
617
638
|
|
|
639
|
+
# Build-time args for public API URLs (populated by gkm deploy)
|
|
640
|
+
# These get baked into the Next.js build as public environment variables
|
|
641
|
+
${publicUrlArgDeclarations}
|
|
642
|
+
|
|
643
|
+
# Convert ARGs to ENVs for Next.js build
|
|
644
|
+
${publicUrlEnvDeclarations}
|
|
645
|
+
|
|
618
646
|
# Copy pruned source
|
|
619
647
|
COPY --from=pruner /app/out/full/ ./
|
|
620
648
|
|
|
@@ -717,9 +745,20 @@ FROM deps AS builder
|
|
|
717
745
|
|
|
718
746
|
WORKDIR /app
|
|
719
747
|
|
|
748
|
+
# Build-time args for encrypted secrets
|
|
749
|
+
ARG GKM_ENCRYPTED_CREDENTIALS=""
|
|
750
|
+
ARG GKM_CREDENTIALS_IV=""
|
|
751
|
+
|
|
720
752
|
# Copy pruned source
|
|
721
753
|
COPY --from=pruner /app/out/full/ ./
|
|
722
754
|
|
|
755
|
+
# Write encrypted credentials for gkm build to embed
|
|
756
|
+
RUN if [ -n "$GKM_ENCRYPTED_CREDENTIALS" ]; then \
|
|
757
|
+
mkdir -p ${appPath}/.gkm && \
|
|
758
|
+
echo "$GKM_ENCRYPTED_CREDENTIALS" > ${appPath}/.gkm/credentials.enc && \
|
|
759
|
+
echo "$GKM_CREDENTIALS_IV" > ${appPath}/.gkm/credentials.iv; \
|
|
760
|
+
fi
|
|
761
|
+
|
|
723
762
|
# Build production server using gkm
|
|
724
763
|
RUN cd ${appPath} && ./node_modules/.bin/gkm build --provider server --production
|
|
725
764
|
|
|
@@ -750,3 +789,134 @@ ENTRYPOINT ["/sbin/tini", "--"]
|
|
|
750
789
|
CMD ["node", "server.mjs"]
|
|
751
790
|
`;
|
|
752
791
|
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Options for entry-based Dockerfile generation.
|
|
795
|
+
*/
|
|
796
|
+
export interface EntryDockerfileOptions {
|
|
797
|
+
imageName: string;
|
|
798
|
+
baseImage: string;
|
|
799
|
+
port: number;
|
|
800
|
+
/** App path relative to workspace root */
|
|
801
|
+
appPath: string;
|
|
802
|
+
/** Entry file path relative to app path (e.g., './src/index.ts') */
|
|
803
|
+
entry: string;
|
|
804
|
+
/** Package name for turbo prune */
|
|
805
|
+
turboPackage: string;
|
|
806
|
+
/** Detected package manager */
|
|
807
|
+
packageManager: PackageManager;
|
|
808
|
+
/** Health check path (default: '/health') */
|
|
809
|
+
healthCheckPath?: string;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Generate a Dockerfile for apps with a custom entry point.
|
|
814
|
+
* Uses tsdown to bundle the entry point into dist/index.mjs.
|
|
815
|
+
* This is used for apps that don't use gkm routes (e.g., Better Auth servers).
|
|
816
|
+
* @internal Exported for testing
|
|
817
|
+
*/
|
|
818
|
+
export function generateEntryDockerfile(options: EntryDockerfileOptions): string {
|
|
819
|
+
const {
|
|
820
|
+
baseImage,
|
|
821
|
+
port,
|
|
822
|
+
appPath,
|
|
823
|
+
entry,
|
|
824
|
+
turboPackage,
|
|
825
|
+
packageManager,
|
|
826
|
+
healthCheckPath = '/health',
|
|
827
|
+
} = options;
|
|
828
|
+
|
|
829
|
+
const pm = getPmConfig(packageManager);
|
|
830
|
+
const installPm = pm.install ? `RUN ${pm.install}` : '';
|
|
831
|
+
const turboInstallCmd = getTurboInstallCmd(packageManager);
|
|
832
|
+
const turboCmd = packageManager === 'pnpm' ? 'pnpm dlx turbo' : 'npx turbo';
|
|
833
|
+
|
|
834
|
+
return `# syntax=docker/dockerfile:1
|
|
835
|
+
# Entry-based Dockerfile with turbo prune + tsdown bundling
|
|
836
|
+
|
|
837
|
+
# Stage 1: Prune monorepo
|
|
838
|
+
FROM ${baseImage} AS pruner
|
|
839
|
+
|
|
840
|
+
WORKDIR /app
|
|
841
|
+
|
|
842
|
+
${installPm}
|
|
843
|
+
|
|
844
|
+
COPY . .
|
|
845
|
+
|
|
846
|
+
# Prune to only include necessary packages
|
|
847
|
+
RUN ${turboCmd} prune ${turboPackage} --docker
|
|
848
|
+
|
|
849
|
+
# Stage 2: Install dependencies
|
|
850
|
+
FROM ${baseImage} AS deps
|
|
851
|
+
|
|
852
|
+
WORKDIR /app
|
|
853
|
+
|
|
854
|
+
${installPm}
|
|
855
|
+
|
|
856
|
+
# Copy pruned lockfile and package.jsons
|
|
857
|
+
COPY --from=pruner /app/out/${pm.lockfile} ./
|
|
858
|
+
COPY --from=pruner /app/out/json/ ./
|
|
859
|
+
|
|
860
|
+
# Install dependencies
|
|
861
|
+
RUN --mount=type=cache,id=${pm.cacheId},target=${pm.cacheTarget} \\
|
|
862
|
+
${turboInstallCmd}
|
|
863
|
+
|
|
864
|
+
# Stage 3: Build with tsdown
|
|
865
|
+
FROM deps AS builder
|
|
866
|
+
|
|
867
|
+
WORKDIR /app
|
|
868
|
+
|
|
869
|
+
# Build-time args for encrypted secrets
|
|
870
|
+
ARG GKM_ENCRYPTED_CREDENTIALS=""
|
|
871
|
+
ARG GKM_CREDENTIALS_IV=""
|
|
872
|
+
|
|
873
|
+
# Copy pruned source
|
|
874
|
+
COPY --from=pruner /app/out/full/ ./
|
|
875
|
+
|
|
876
|
+
# Write encrypted credentials for tsdown to embed via define
|
|
877
|
+
RUN if [ -n "$GKM_ENCRYPTED_CREDENTIALS" ]; then \
|
|
878
|
+
mkdir -p ${appPath}/.gkm && \
|
|
879
|
+
echo "$GKM_ENCRYPTED_CREDENTIALS" > ${appPath}/.gkm/credentials.enc && \
|
|
880
|
+
echo "$GKM_CREDENTIALS_IV" > ${appPath}/.gkm/credentials.iv; \
|
|
881
|
+
fi
|
|
882
|
+
|
|
883
|
+
# Bundle entry point with tsdown (outputs to dist/index.mjs)
|
|
884
|
+
# Use define to embed credentials if present
|
|
885
|
+
RUN cd ${appPath} && \
|
|
886
|
+
if [ -f .gkm/credentials.enc ]; then \
|
|
887
|
+
CREDS=$(cat .gkm/credentials.enc) && \
|
|
888
|
+
IV=$(cat .gkm/credentials.iv) && \
|
|
889
|
+
npx tsdown ${entry} --outDir dist --format esm \
|
|
890
|
+
--define __GKM_ENCRYPTED_CREDENTIALS__="'\\"$CREDS\\"'" \
|
|
891
|
+
--define __GKM_CREDENTIALS_IV__="'\\"$IV\\"'"; \
|
|
892
|
+
else \
|
|
893
|
+
npx tsdown ${entry} --outDir dist --format esm; \
|
|
894
|
+
fi
|
|
895
|
+
|
|
896
|
+
# Stage 4: Production
|
|
897
|
+
FROM ${baseImage} AS runner
|
|
898
|
+
|
|
899
|
+
WORKDIR /app
|
|
900
|
+
|
|
901
|
+
RUN apk add --no-cache tini
|
|
902
|
+
|
|
903
|
+
RUN addgroup --system --gid 1001 nodejs && \\
|
|
904
|
+
adduser --system --uid 1001 app
|
|
905
|
+
|
|
906
|
+
# Copy bundled output only (no node_modules needed - fully bundled)
|
|
907
|
+
COPY --from=builder --chown=app:nodejs /app/${appPath}/dist/index.mjs ./
|
|
908
|
+
|
|
909
|
+
ENV NODE_ENV=production
|
|
910
|
+
ENV PORT=${port}
|
|
911
|
+
|
|
912
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
913
|
+
CMD wget -q --spider http://localhost:${port}${healthCheckPath} || exit 1
|
|
914
|
+
|
|
915
|
+
USER app
|
|
916
|
+
|
|
917
|
+
EXPOSE ${port}
|
|
918
|
+
|
|
919
|
+
ENTRYPOINT ["/sbin/tini", "--"]
|
|
920
|
+
CMD ["node", "index.mjs"]
|
|
921
|
+
`;
|
|
922
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { loginCommand, logoutCommand, whoamiCommand } from './auth';
|
|
|
6
6
|
import { buildCommand } from './build/index';
|
|
7
7
|
import { type DeployProvider, deployCommand } from './deploy/index';
|
|
8
8
|
import { deployInitCommand, deployListCommand } from './deploy/init';
|
|
9
|
-
import { devCommand } from './dev/index';
|
|
9
|
+
import { devCommand, execCommand } from './dev/index';
|
|
10
10
|
import { type DockerOptions, dockerCommand } from './docker/index';
|
|
11
11
|
import { type InitOptions, initCommand } from './init/index';
|
|
12
12
|
import { openapiCommand } from './openapi';
|
|
@@ -173,6 +173,23 @@ program
|
|
|
173
173
|
},
|
|
174
174
|
);
|
|
175
175
|
|
|
176
|
+
program
|
|
177
|
+
.command('exec')
|
|
178
|
+
.description('Run a command with secrets injected into Credentials')
|
|
179
|
+
.argument('<command...>', 'Command to run (use -- before command)')
|
|
180
|
+
.action(async (commandArgs: string[]) => {
|
|
181
|
+
try {
|
|
182
|
+
const globalOptions = program.opts();
|
|
183
|
+
if (globalOptions.cwd) {
|
|
184
|
+
process.chdir(globalOptions.cwd);
|
|
185
|
+
}
|
|
186
|
+
await execCommand(commandArgs);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(error instanceof Error ? error.message : 'Command failed');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
176
193
|
program
|
|
177
194
|
.command('test')
|
|
178
195
|
.description('Run tests with secrets loaded from environment')
|
|
@@ -26,6 +26,8 @@ export function generateAuthAppFiles(
|
|
|
26
26
|
build: 'tsc',
|
|
27
27
|
start: 'node dist/index.js',
|
|
28
28
|
typecheck: 'tsc --noEmit',
|
|
29
|
+
'db:migrate': 'gkm exec -- npx @better-auth/cli migrate',
|
|
30
|
+
'db:generate': 'gkm exec -- npx @better-auth/cli generate',
|
|
29
31
|
},
|
|
30
32
|
dependencies: {
|
|
31
33
|
[modelsPackage]: 'workspace:*',
|
package/src/init/versions.ts
CHANGED
|
@@ -32,10 +32,10 @@ export const GEEKMIDAS_VERSIONS = {
|
|
|
32
32
|
'@geekmidas/cli': CLI_VERSION,
|
|
33
33
|
'@geekmidas/client': '~0.5.0',
|
|
34
34
|
'@geekmidas/cloud': '~0.2.0',
|
|
35
|
-
'@geekmidas/constructs': '~0.
|
|
35
|
+
'@geekmidas/constructs': '~0.7.0',
|
|
36
36
|
'@geekmidas/db': '~0.3.0',
|
|
37
37
|
'@geekmidas/emailkit': '~0.2.0',
|
|
38
|
-
'@geekmidas/envkit': '~0.
|
|
38
|
+
'@geekmidas/envkit': '~0.6.0',
|
|
39
39
|
'@geekmidas/errors': '~0.1.0',
|
|
40
40
|
'@geekmidas/events': '~0.2.0',
|
|
41
41
|
'@geekmidas/logger': '~0.4.0',
|
package/src/workspace/index.ts
CHANGED
|
@@ -34,11 +34,13 @@ export type {
|
|
|
34
34
|
AppConfigInput,
|
|
35
35
|
AppInput,
|
|
36
36
|
AppsRecord,
|
|
37
|
+
BackendFramework,
|
|
37
38
|
ClientConfig,
|
|
38
39
|
ConstrainedApps,
|
|
39
40
|
DeployConfig,
|
|
40
41
|
DeployTarget,
|
|
41
42
|
DokployWorkspaceConfig,
|
|
43
|
+
FrontendFramework,
|
|
42
44
|
InferAppNames,
|
|
43
45
|
InferredWorkspaceConfig,
|
|
44
46
|
LoadedConfig,
|
package/src/workspace/schema.ts
CHANGED
|
@@ -57,6 +57,26 @@ const ClientConfigSchema = z.object({
|
|
|
57
57
|
*/
|
|
58
58
|
const AuthProviderSchema = z.enum(['better-auth']);
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Backend framework schema for non-gkm apps.
|
|
62
|
+
*/
|
|
63
|
+
const BackendFrameworkSchema = z.enum([
|
|
64
|
+
'hono',
|
|
65
|
+
'better-auth',
|
|
66
|
+
'express',
|
|
67
|
+
'fastify',
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Frontend framework schema.
|
|
72
|
+
*/
|
|
73
|
+
const FrontendFrameworkSchema = z.enum(['nextjs', 'remix', 'vite']);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Combined framework schema (backend or frontend).
|
|
77
|
+
*/
|
|
78
|
+
const FrameworkSchema = z.union([BackendFrameworkSchema, FrontendFrameworkSchema]);
|
|
79
|
+
|
|
60
80
|
/**
|
|
61
81
|
* Deploy target schema.
|
|
62
82
|
* Currently only 'dokploy' is supported.
|
|
@@ -205,8 +225,11 @@ const AppConfigSchema = z
|
|
|
205
225
|
runtime: z.enum(['node', 'bun']).optional(),
|
|
206
226
|
env: z.union([z.string(), z.array(z.string())]).optional(),
|
|
207
227
|
|
|
208
|
-
//
|
|
209
|
-
|
|
228
|
+
// Entry point for non-gkm apps (used by dev and docker build)
|
|
229
|
+
entry: z.string().optional(),
|
|
230
|
+
|
|
231
|
+
// Framework (backend or frontend)
|
|
232
|
+
framework: FrameworkSchema.optional(),
|
|
210
233
|
client: ClientConfigSchema.optional(),
|
|
211
234
|
|
|
212
235
|
// Auth-specific
|
|
@@ -215,14 +238,17 @@ const AppConfigSchema = z
|
|
|
215
238
|
// Note: routes is optional for backend apps - some backends like auth servers don't use routes
|
|
216
239
|
.refine(
|
|
217
240
|
(data) => {
|
|
218
|
-
// Frontend apps must have framework
|
|
219
|
-
if (data.type === 'frontend'
|
|
220
|
-
|
|
241
|
+
// Frontend apps must have a frontend framework
|
|
242
|
+
if (data.type === 'frontend') {
|
|
243
|
+
const frontendFrameworks = ['nextjs', 'remix', 'vite'];
|
|
244
|
+
if (!data.framework || !frontendFrameworks.includes(data.framework)) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
221
247
|
}
|
|
222
248
|
return true;
|
|
223
249
|
},
|
|
224
250
|
{
|
|
225
|
-
message: 'Frontend apps must have framework
|
|
251
|
+
message: 'Frontend apps must have a valid frontend framework (nextjs, remix, vite)',
|
|
226
252
|
path: ['framework'],
|
|
227
253
|
},
|
|
228
254
|
)
|
package/src/workspace/types.ts
CHANGED
|
@@ -16,6 +16,16 @@ import type {
|
|
|
16
16
|
*/
|
|
17
17
|
export type DeployTarget = 'dokploy' | 'vercel' | 'cloudflare';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Backend framework types for apps that don't use gkm routes.
|
|
21
|
+
*/
|
|
22
|
+
export type BackendFramework = 'hono' | 'better-auth' | 'express' | 'fastify';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Frontend framework types.
|
|
26
|
+
*/
|
|
27
|
+
export type FrontendFramework = 'nextjs' | 'remix' | 'vite';
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* Service image configuration for custom Docker images.
|
|
21
31
|
*/
|
|
@@ -51,6 +61,20 @@ export interface ServicesConfig {
|
|
|
51
61
|
mail?: boolean | MailServiceConfig;
|
|
52
62
|
}
|
|
53
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Stage-based domain configuration.
|
|
66
|
+
* Maps deployment stages to base domains.
|
|
67
|
+
* @example { development: 'dev.myapp.com', production: 'myapp.com' }
|
|
68
|
+
*/
|
|
69
|
+
export type DokployDomainsConfig = Record<string, string>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Per-app domain override configuration.
|
|
73
|
+
* Can be a single domain string or stage-specific domains.
|
|
74
|
+
* @example 'api.custom.com' or { production: 'api.custom.com', staging: 'api.staging.com' }
|
|
75
|
+
*/
|
|
76
|
+
export type AppDomainConfig = string | Record<string, string>;
|
|
77
|
+
|
|
54
78
|
/**
|
|
55
79
|
* Dokploy workspace deployment configuration.
|
|
56
80
|
*/
|
|
@@ -63,6 +87,13 @@ export interface DokployWorkspaceConfig {
|
|
|
63
87
|
registry?: string;
|
|
64
88
|
/** Registry ID in Dokploy */
|
|
65
89
|
registryId?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Stage-based domain configuration.
|
|
92
|
+
* The main frontend app gets the base domain.
|
|
93
|
+
* Other apps get {appName}.{baseDomain} by default.
|
|
94
|
+
* @example { development: 'dev.myapp.com', production: 'myapp.com' }
|
|
95
|
+
*/
|
|
96
|
+
domains?: DokployDomainsConfig;
|
|
66
97
|
}
|
|
67
98
|
|
|
68
99
|
/**
|
|
@@ -163,11 +194,34 @@ interface AppConfigBase {
|
|
|
163
194
|
/** Environment file(s) to load */
|
|
164
195
|
env?: string | string[];
|
|
165
196
|
|
|
197
|
+
// Entry point for non-gkm apps
|
|
198
|
+
/**
|
|
199
|
+
* Entry file path for apps that don't use gkm routes.
|
|
200
|
+
* Used by both `gkm dev` (runs with tsx) and Docker builds (bundles with tsdown).
|
|
201
|
+
* @example './src/index.ts'
|
|
202
|
+
*/
|
|
203
|
+
entry?: string;
|
|
204
|
+
|
|
166
205
|
// Frontend-specific
|
|
167
|
-
/**
|
|
168
|
-
framework?:
|
|
206
|
+
/** Framework for the app (frontend or backend without gkm routes) */
|
|
207
|
+
framework?: BackendFramework | FrontendFramework;
|
|
169
208
|
/** Client generation configuration */
|
|
170
209
|
client?: ClientConfig;
|
|
210
|
+
|
|
211
|
+
// Deployment
|
|
212
|
+
/**
|
|
213
|
+
* Override domain for this app (per-stage or single value).
|
|
214
|
+
* @example 'api.custom.com' or { production: 'api.custom.com', staging: 'api.staging.com' }
|
|
215
|
+
*/
|
|
216
|
+
domain?: AppDomainConfig;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Required environment variables for entry-based apps.
|
|
220
|
+
* Use this instead of envParser for apps that don't use gkm routes.
|
|
221
|
+
* The deploy command uses this to filter which secrets to embed.
|
|
222
|
+
* @example ['DATABASE_URL', 'BETTER_AUTH_SECRET']
|
|
223
|
+
*/
|
|
224
|
+
requiredEnv?: string[];
|
|
171
225
|
}
|
|
172
226
|
|
|
173
227
|
/**
|
|
@@ -294,6 +348,14 @@ export interface NormalizedAppConfig extends Omit<AppConfigBase, 'type'> {
|
|
|
294
348
|
dependencies: string[];
|
|
295
349
|
/** Resolved deploy target (app.deploy > deploy.default > 'dokploy') */
|
|
296
350
|
resolvedDeployTarget: DeployTarget;
|
|
351
|
+
/** Entry file path for non-gkm apps */
|
|
352
|
+
entry?: string;
|
|
353
|
+
/** Framework for the app */
|
|
354
|
+
framework?: BackendFramework | FrontendFramework;
|
|
355
|
+
/** Override domain for this app */
|
|
356
|
+
domain?: AppDomainConfig;
|
|
357
|
+
/** Required environment variables for entry-based apps */
|
|
358
|
+
requiredEnv?: string[];
|
|
297
359
|
}
|
|
298
360
|
|
|
299
361
|
/**
|