@geekmidas/cli 0.54.0 → 1.0.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 +23 -0
- package/README.md +26 -5
- package/dist/CachedStateProvider-D73dCqfH.cjs +60 -0
- package/dist/CachedStateProvider-D73dCqfH.cjs.map +1 -0
- package/dist/CachedStateProvider-DVyKfaMm.mjs +54 -0
- package/dist/CachedStateProvider-DVyKfaMm.mjs.map +1 -0
- package/dist/CachedStateProvider-D_uISMmJ.cjs +3 -0
- package/dist/CachedStateProvider-OiFUGr7p.mjs +3 -0
- package/dist/HostingerProvider-DUV9-Tzg.cjs +210 -0
- package/dist/HostingerProvider-DUV9-Tzg.cjs.map +1 -0
- package/dist/HostingerProvider-DqUq6e9i.mjs +210 -0
- package/dist/HostingerProvider-DqUq6e9i.mjs.map +1 -0
- package/dist/LocalStateProvider-CdspeSVL.cjs +43 -0
- package/dist/LocalStateProvider-CdspeSVL.cjs.map +1 -0
- package/dist/LocalStateProvider-DxoSaWUV.mjs +42 -0
- package/dist/LocalStateProvider-DxoSaWUV.mjs.map +1 -0
- package/dist/Route53Provider-CpRIqu69.cjs +157 -0
- package/dist/Route53Provider-CpRIqu69.cjs.map +1 -0
- package/dist/Route53Provider-KUAX3vz9.mjs +156 -0
- package/dist/Route53Provider-KUAX3vz9.mjs.map +1 -0
- package/dist/SSMStateProvider-BxAPU99a.cjs +53 -0
- package/dist/SSMStateProvider-BxAPU99a.cjs.map +1 -0
- package/dist/SSMStateProvider-C4wp4AZe.mjs +52 -0
- package/dist/SSMStateProvider-C4wp4AZe.mjs.map +1 -0
- package/dist/{bundler-DGry2vaR.mjs → bundler-BqTN5Dj5.mjs} +3 -3
- package/dist/{bundler-DGry2vaR.mjs.map → bundler-BqTN5Dj5.mjs.map} +1 -1
- package/dist/{bundler-BB-kETMd.cjs → bundler-tHLLwYuU.cjs} +3 -3
- package/dist/{bundler-BB-kETMd.cjs.map → bundler-tHLLwYuU.cjs.map} +1 -1
- package/dist/{config-HYiM3iQJ.cjs → config-BGeJsW1r.cjs} +2 -2
- package/dist/{config-HYiM3iQJ.cjs.map → config-BGeJsW1r.cjs.map} +1 -1
- package/dist/{config-C3LSBNSl.mjs → config-C6awcFBx.mjs} +2 -2
- package/dist/{config-C3LSBNSl.mjs.map → config-C6awcFBx.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/credentials-C8DWtnMY.cjs +174 -0
- package/dist/credentials-C8DWtnMY.cjs.map +1 -0
- package/dist/credentials-DT1dSxIx.mjs +126 -0
- package/dist/credentials-DT1dSxIx.mjs.map +1 -0
- package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
- package/dist/deploy/sniffer-loader.cjs +1 -1
- package/dist/{dokploy-api-94KzmTVf.mjs → dokploy-api-7k3t7_zd.mjs} +1 -1
- package/dist/{dokploy-api-94KzmTVf.mjs.map → dokploy-api-7k3t7_zd.mjs.map} +1 -1
- package/dist/dokploy-api-CHa8G51l.mjs +3 -0
- package/dist/{dokploy-api-YD8WCQfW.cjs → dokploy-api-CQvhV6Hd.cjs} +1 -1
- package/dist/{dokploy-api-YD8WCQfW.cjs.map → dokploy-api-CQvhV6Hd.cjs.map} +1 -1
- package/dist/dokploy-api-CWc02yyg.cjs +3 -0
- package/dist/{encryption-DaCB_NmS.cjs → encryption-BE0UOb8j.cjs} +1 -1
- package/dist/{encryption-DaCB_NmS.cjs.map → encryption-BE0UOb8j.cjs.map} +1 -1
- package/dist/{encryption-Biq0EZ4m.cjs → encryption-Cv3zips0.cjs} +1 -1
- package/dist/{encryption-BC4MAODn.mjs → encryption-JtMsiGNp.mjs} +1 -1
- package/dist/{encryption-BC4MAODn.mjs.map → encryption-JtMsiGNp.mjs.map} +1 -1
- package/dist/encryption-UUmaWAmz.mjs +3 -0
- package/dist/{index-pOA56MWT.d.cts → index-B5rGIc4g.d.cts} +553 -196
- package/dist/index-B5rGIc4g.d.cts.map +1 -0
- package/dist/{index-A70abJ1m.d.mts → index-KFEbMIRa.d.mts} +554 -197
- package/dist/index-KFEbMIRa.d.mts.map +1 -0
- package/dist/index.cjs +2265 -658
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2242 -635
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-C3C-BzIZ.mjs → openapi-BMFmLnX6.mjs} +51 -7
- package/dist/openapi-BMFmLnX6.mjs.map +1 -0
- package/dist/{openapi-D7WwlpPF.cjs → openapi-D1KXv2Ml.cjs} +51 -7
- package/dist/openapi-D1KXv2Ml.cjs.map +1 -0
- package/dist/{openapi-react-query-C_MxpBgF.cjs → openapi-react-query-BeXvk-wa.cjs} +1 -1
- package/dist/{openapi-react-query-C_MxpBgF.cjs.map → openapi-react-query-BeXvk-wa.cjs.map} +1 -1
- package/dist/{openapi-react-query-ZoP9DPbY.mjs → openapi-react-query-DGEkD39r.mjs} +1 -1
- package/dist/{openapi-react-query-ZoP9DPbY.mjs.map → openapi-react-query-DGEkD39r.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.cts +1 -1
- package/dist/openapi.d.mts +2 -2
- package/dist/openapi.mjs +3 -3
- package/dist/{storage-Dhst7BhI.mjs → storage-BMW6yLu3.mjs} +1 -1
- package/dist/{storage-Dhst7BhI.mjs.map → storage-BMW6yLu3.mjs.map} +1 -1
- package/dist/{storage-fOR8dMu5.cjs → storage-C7pmBq1u.cjs} +1 -1
- package/dist/{storage-BPRgh3DU.cjs → storage-CoCNe0Pt.cjs} +1 -1
- package/dist/{storage-BPRgh3DU.cjs.map → storage-CoCNe0Pt.cjs.map} +1 -1
- package/dist/{storage-DNj_I11J.mjs → storage-D8XzjVaO.mjs} +1 -1
- package/dist/{types-BtGL-8QS.d.mts → types-BldpmqQX.d.mts} +1 -1
- package/dist/{types-BtGL-8QS.d.mts.map → types-BldpmqQX.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-CaVW6j2q.cjs → workspace-BFRUOOrh.cjs} +309 -25
- package/dist/workspace-BFRUOOrh.cjs.map +1 -0
- package/dist/{workspace-DLFRaDc-.mjs → workspace-DAxG3_H2.mjs} +309 -25
- package/dist/workspace-DAxG3_H2.mjs.map +1 -0
- package/package.json +14 -8
- package/scripts/sync-versions.ts +86 -0
- package/src/build/__tests__/handler-templates.spec.ts +115 -47
- package/src/deploy/CachedStateProvider.ts +86 -0
- package/src/deploy/LocalStateProvider.ts +57 -0
- package/src/deploy/SSMStateProvider.ts +93 -0
- package/src/deploy/StateProvider.ts +171 -0
- package/src/deploy/__tests__/CachedStateProvider.spec.ts +228 -0
- package/src/deploy/__tests__/HostingerProvider.spec.ts +347 -0
- package/src/deploy/__tests__/LocalStateProvider.spec.ts +126 -0
- package/src/deploy/__tests__/Route53Provider.spec.ts +402 -0
- package/src/deploy/__tests__/SSMStateProvider.spec.ts +177 -0
- package/src/deploy/__tests__/__fixtures__/env-parsers/throwing-env-parser.ts +1 -3
- package/src/deploy/__tests__/__fixtures__/route-apps/services.ts +28 -19
- package/src/deploy/__tests__/createDnsProvider.spec.ts +172 -0
- package/src/deploy/__tests__/createStateProvider.spec.ts +116 -0
- package/src/deploy/__tests__/dns-orchestration.spec.ts +192 -0
- package/src/deploy/__tests__/dns-verification.spec.ts +2 -2
- package/src/deploy/__tests__/env-resolver.spec.ts +37 -15
- package/src/deploy/__tests__/sniffer.spec.ts +4 -20
- package/src/deploy/__tests__/state.spec.ts +13 -5
- package/src/deploy/dns/DnsProvider.ts +163 -0
- package/src/deploy/dns/HostingerProvider.ts +100 -0
- package/src/deploy/dns/Route53Provider.ts +256 -0
- package/src/deploy/dns/index.ts +257 -165
- package/src/deploy/env-resolver.ts +12 -5
- package/src/deploy/index.ts +16 -13
- package/src/deploy/sniffer-envkit-patch.ts +3 -1
- package/src/deploy/sniffer-routes-worker.ts +104 -0
- package/src/deploy/sniffer.ts +77 -55
- package/src/deploy/state-commands.ts +274 -0
- package/src/dev/__tests__/entry.spec.ts +8 -2
- package/src/dev/__tests__/index.spec.ts +1 -3
- package/src/dev/index.ts +9 -3
- package/src/docker/__tests__/templates.spec.ts +3 -1
- package/src/index.ts +88 -0
- package/src/init/__tests__/generators.spec.ts +273 -0
- package/src/init/__tests__/init.spec.ts +3 -3
- package/src/init/generators/auth.ts +1 -0
- package/src/init/generators/config.ts +2 -0
- package/src/init/generators/models.ts +6 -1
- package/src/init/generators/monorepo.ts +3 -0
- package/src/init/generators/ui.ts +1472 -0
- package/src/init/generators/web.ts +134 -87
- package/src/init/index.ts +22 -3
- package/src/init/templates/api.ts +109 -3
- package/src/init/versions.ts +25 -53
- package/src/openapi.ts +99 -13
- package/src/workspace/__tests__/schema.spec.ts +107 -0
- package/src/workspace/schema.ts +314 -4
- package/src/workspace/types.ts +22 -36
- package/dist/dokploy-api-CItuaWTq.mjs +0 -3
- package/dist/dokploy-api-DBNE8MDt.cjs +0 -3
- package/dist/encryption-CQXBZGkt.mjs +0 -3
- package/dist/index-A70abJ1m.d.mts.map +0 -1
- package/dist/index-pOA56MWT.d.cts.map +0 -1
- package/dist/openapi-C3C-BzIZ.mjs.map +0 -1
- package/dist/openapi-D7WwlpPF.cjs.map +0 -1
- package/dist/workspace-CaVW6j2q.cjs.map +0 -1
- package/dist/workspace-DLFRaDc-.mjs.map +0 -1
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -11,6 +11,7 @@ export function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {
|
|
|
11
11
|
|
|
12
12
|
const packageName = `@${options.name}/web`;
|
|
13
13
|
const modelsPackage = `@${options.name}/models`;
|
|
14
|
+
const uiPackage = `@${options.name}/ui`;
|
|
14
15
|
|
|
15
16
|
// package.json for web app
|
|
16
17
|
const packageJson = {
|
|
@@ -19,23 +20,28 @@ export function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {
|
|
|
19
20
|
private: true,
|
|
20
21
|
type: 'module',
|
|
21
22
|
scripts: {
|
|
22
|
-
dev: 'next dev
|
|
23
|
-
build: 'next build',
|
|
23
|
+
dev: 'gkm exec -- next dev --turbopack',
|
|
24
|
+
build: 'gkm exec -- next build',
|
|
24
25
|
start: 'next start',
|
|
25
26
|
typecheck: 'tsc --noEmit',
|
|
26
27
|
},
|
|
27
28
|
dependencies: {
|
|
28
29
|
[modelsPackage]: 'workspace:*',
|
|
30
|
+
[uiPackage]: 'workspace:*',
|
|
29
31
|
'@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],
|
|
30
32
|
'@tanstack/react-query': '~5.80.0',
|
|
33
|
+
'better-auth': '~1.2.0',
|
|
31
34
|
next: '~16.1.0',
|
|
32
35
|
react: '~19.2.0',
|
|
33
36
|
'react-dom': '~19.2.0',
|
|
34
37
|
},
|
|
35
38
|
devDependencies: {
|
|
39
|
+
'@geekmidas/cli': GEEKMIDAS_VERSIONS['@geekmidas/cli'],
|
|
40
|
+
'@tailwindcss/postcss': '^4.0.0',
|
|
36
41
|
'@types/node': '~22.0.0',
|
|
37
42
|
'@types/react': '~19.0.0',
|
|
38
43
|
'@types/react-dom': '~19.0.0',
|
|
44
|
+
tailwindcss: '^4.0.0',
|
|
39
45
|
typescript: '~5.8.2',
|
|
40
46
|
},
|
|
41
47
|
};
|
|
@@ -46,10 +52,18 @@ export function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {
|
|
|
46
52
|
const nextConfig: NextConfig = {
|
|
47
53
|
output: 'standalone',
|
|
48
54
|
reactStrictMode: true,
|
|
49
|
-
transpilePackages: ['${modelsPackage}'],
|
|
55
|
+
transpilePackages: ['${modelsPackage}', '${uiPackage}'],
|
|
50
56
|
};
|
|
51
57
|
|
|
52
58
|
export default nextConfig;
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
// postcss.config.mjs for Tailwind v4
|
|
62
|
+
const postcssConfig = `export default {
|
|
63
|
+
plugins: {
|
|
64
|
+
'@tailwindcss/postcss': {},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
53
67
|
`;
|
|
54
68
|
|
|
55
69
|
// tsconfig.json for Next.js
|
|
@@ -75,9 +89,11 @@ export default nextConfig;
|
|
|
75
89
|
},
|
|
76
90
|
],
|
|
77
91
|
paths: {
|
|
78
|
-
'
|
|
92
|
+
'~/*': ['./src/*'],
|
|
79
93
|
[`${modelsPackage}`]: ['../../packages/models/src'],
|
|
80
94
|
[`${modelsPackage}/*`]: ['../../packages/models/src/*'],
|
|
95
|
+
[`${uiPackage}`]: ['../../packages/ui/src'],
|
|
96
|
+
[`${uiPackage}/*`]: ['../../packages/ui/src/*'],
|
|
81
97
|
},
|
|
82
98
|
baseUrl: '.',
|
|
83
99
|
},
|
|
@@ -85,23 +101,52 @@ export default nextConfig;
|
|
|
85
101
|
exclude: ['node_modules'],
|
|
86
102
|
};
|
|
87
103
|
|
|
88
|
-
//
|
|
104
|
+
// Query client singleton for browser, fresh instance for server
|
|
105
|
+
const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
|
|
106
|
+
|
|
107
|
+
function makeQueryClient() {
|
|
108
|
+
return new QueryClient({
|
|
109
|
+
defaultOptions: {
|
|
110
|
+
queries: {
|
|
111
|
+
staleTime: 60 * 1000,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let browserQueryClient: QueryClient | undefined = undefined;
|
|
118
|
+
|
|
119
|
+
export function getQueryClient() {
|
|
120
|
+
if (typeof window === 'undefined') {
|
|
121
|
+
// Server: always make a new query client
|
|
122
|
+
return makeQueryClient();
|
|
123
|
+
}
|
|
124
|
+
// Browser: reuse existing query client
|
|
125
|
+
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
|
126
|
+
return browserQueryClient;
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// Auth client for better-auth
|
|
131
|
+
const authClientTs = `import { createAuthClient } from 'better-auth/react';
|
|
132
|
+
import { magicLinkClient } from 'better-auth/client/plugins';
|
|
133
|
+
|
|
134
|
+
export const authClient = createAuthClient({
|
|
135
|
+
baseURL: process.env.NEXT_PUBLIC_AUTH_URL || 'http://localhost:3002',
|
|
136
|
+
plugins: [magicLinkClient()],
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
export const { signIn, signUp, signOut, useSession, magicLink } = authClient;
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
// Providers using shared QueryClient
|
|
89
143
|
const providersTsx = `'use client';
|
|
90
144
|
|
|
91
|
-
import {
|
|
92
|
-
import {
|
|
145
|
+
import { QueryClientProvider } from '@tanstack/react-query';
|
|
146
|
+
import { getQueryClient } from '~/lib/query-client';
|
|
93
147
|
|
|
94
148
|
export function Providers({ children }: { children: React.ReactNode }) {
|
|
95
|
-
const
|
|
96
|
-
() =>
|
|
97
|
-
new QueryClient({
|
|
98
|
-
defaultOptions: {
|
|
99
|
-
queries: {
|
|
100
|
-
staleTime: 60 * 1000,
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
}),
|
|
104
|
-
);
|
|
149
|
+
const queryClient = getQueryClient();
|
|
105
150
|
|
|
106
151
|
return (
|
|
107
152
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
@@ -109,49 +154,24 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
109
154
|
}
|
|
110
155
|
`;
|
|
111
156
|
|
|
112
|
-
// API client setup
|
|
113
|
-
const apiIndexTs = `import {
|
|
114
|
-
import {
|
|
115
|
-
|
|
116
|
-
// TODO: Run 'gkm openapi' to generate typed paths from your API
|
|
117
|
-
// This is a placeholder that will be replaced by the generated openapi.ts
|
|
118
|
-
interface paths {
|
|
119
|
-
'/health': {
|
|
120
|
-
get: {
|
|
121
|
-
responses: {
|
|
122
|
-
200: {
|
|
123
|
-
content: {
|
|
124
|
-
'application/json': { status: string; timestamp: string };
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
};
|
|
128
|
-
};
|
|
129
|
-
};
|
|
130
|
-
'/users': {
|
|
131
|
-
get: {
|
|
132
|
-
responses: {
|
|
133
|
-
200: {
|
|
134
|
-
content: {
|
|
135
|
-
'application/json': { users: Array<{ id: string; name: string }> };
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const baseURL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
|
157
|
+
// API client setup - uses createApi with shared QueryClient
|
|
158
|
+
const apiIndexTs = `import { createApi } from './openapi';
|
|
159
|
+
import { getQueryClient } from '~/lib/query-client';
|
|
144
160
|
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
export const api = createApi({
|
|
162
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
|
|
163
|
+
queryClient: getQueryClient(),
|
|
164
|
+
});
|
|
165
|
+
`;
|
|
148
166
|
|
|
149
|
-
|
|
167
|
+
// globals.css that imports UI package styles
|
|
168
|
+
const globalsCss = `@import '${uiPackage}/styles';
|
|
150
169
|
`;
|
|
151
170
|
|
|
152
171
|
// App layout
|
|
153
172
|
const layoutTsx = `import type { Metadata } from 'next';
|
|
154
173
|
import { Providers } from './providers';
|
|
174
|
+
import './globals.css';
|
|
155
175
|
|
|
156
176
|
export const metadata: Metadata = {
|
|
157
177
|
title: '${options.name}',
|
|
@@ -173,45 +193,60 @@ export default function RootLayout({
|
|
|
173
193
|
}
|
|
174
194
|
`;
|
|
175
195
|
|
|
176
|
-
// Home page with API example
|
|
177
|
-
const pageTsx = `import { api } from '
|
|
196
|
+
// Home page with API example using UI components
|
|
197
|
+
const pageTsx = `import { api } from '~/api';
|
|
198
|
+
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '${uiPackage}/components';
|
|
178
199
|
|
|
179
200
|
export default async function Home() {
|
|
180
201
|
// Type-safe API call using the generated client
|
|
181
202
|
const health = await api('GET /health').catch(() => null);
|
|
182
203
|
|
|
183
204
|
return (
|
|
184
|
-
<main
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
</
|
|
206
|
-
|
|
205
|
+
<main className="min-h-screen bg-background p-8">
|
|
206
|
+
<div className="mx-auto max-w-4xl space-y-8">
|
|
207
|
+
<div className="space-y-2">
|
|
208
|
+
<h1 className="text-4xl font-bold tracking-tight">Welcome to ${options.name}</h1>
|
|
209
|
+
<p className="text-muted-foreground">Your fullstack application is ready.</p>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<Card>
|
|
213
|
+
<CardHeader>
|
|
214
|
+
<CardTitle>API Status</CardTitle>
|
|
215
|
+
<CardDescription>Connection to your backend API</CardDescription>
|
|
216
|
+
</CardHeader>
|
|
217
|
+
<CardContent>
|
|
218
|
+
{health ? (
|
|
219
|
+
<pre className="rounded-lg bg-muted p-4 text-sm">
|
|
220
|
+
{JSON.stringify(health, null, 2)}
|
|
221
|
+
</pre>
|
|
222
|
+
) : (
|
|
223
|
+
<p className="text-destructive">Unable to connect to API</p>
|
|
224
|
+
)}
|
|
225
|
+
</CardContent>
|
|
226
|
+
</Card>
|
|
227
|
+
|
|
228
|
+
<Card>
|
|
229
|
+
<CardHeader>
|
|
230
|
+
<CardTitle>Next Steps</CardTitle>
|
|
231
|
+
<CardDescription>Get started with your project</CardDescription>
|
|
232
|
+
</CardHeader>
|
|
233
|
+
<CardContent className="space-y-4">
|
|
234
|
+
<ul className="list-inside list-disc space-y-2 text-muted-foreground">
|
|
235
|
+
<li>Run <code className="rounded bg-muted px-1">gkm openapi</code> to generate typed API client</li>
|
|
236
|
+
<li>Edit <code className="rounded bg-muted px-1">apps/web/src/app/page.tsx</code> to customize this page</li>
|
|
237
|
+
<li>Add API routes in <code className="rounded bg-muted px-1">apps/api/src/endpoints/</code></li>
|
|
238
|
+
<li>Add UI components with <code className="rounded bg-muted px-1">npx shadcn@latest add</code> in packages/ui</li>
|
|
239
|
+
</ul>
|
|
240
|
+
<div className="flex gap-4">
|
|
241
|
+
<Button>Get Started</Button>
|
|
242
|
+
<Button variant="outline">Documentation</Button>
|
|
243
|
+
</div>
|
|
244
|
+
</CardContent>
|
|
245
|
+
</Card>
|
|
246
|
+
</div>
|
|
207
247
|
</main>
|
|
208
248
|
);
|
|
209
249
|
}
|
|
210
|
-
`;
|
|
211
|
-
|
|
212
|
-
// Environment file for web app
|
|
213
|
-
const envLocal = `# API URL for client-side requests
|
|
214
|
-
NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
215
250
|
`;
|
|
216
251
|
|
|
217
252
|
// .gitignore for Next.js
|
|
@@ -230,10 +265,18 @@ node_modules/
|
|
|
230
265
|
path: 'apps/web/next.config.ts',
|
|
231
266
|
content: nextConfig,
|
|
232
267
|
},
|
|
268
|
+
{
|
|
269
|
+
path: 'apps/web/postcss.config.mjs',
|
|
270
|
+
content: postcssConfig,
|
|
271
|
+
},
|
|
233
272
|
{
|
|
234
273
|
path: 'apps/web/tsconfig.json',
|
|
235
274
|
content: `${JSON.stringify(tsConfig, null, 2)}\n`,
|
|
236
275
|
},
|
|
276
|
+
{
|
|
277
|
+
path: 'apps/web/src/app/globals.css',
|
|
278
|
+
content: globalsCss,
|
|
279
|
+
},
|
|
237
280
|
{
|
|
238
281
|
path: 'apps/web/src/app/layout.tsx',
|
|
239
282
|
content: layoutTsx,
|
|
@@ -247,12 +290,16 @@ node_modules/
|
|
|
247
290
|
content: pageTsx,
|
|
248
291
|
},
|
|
249
292
|
{
|
|
250
|
-
path: 'apps/web/src/
|
|
251
|
-
content:
|
|
293
|
+
path: 'apps/web/src/lib/query-client.ts',
|
|
294
|
+
content: queryClientTs,
|
|
252
295
|
},
|
|
253
296
|
{
|
|
254
|
-
path: 'apps/web
|
|
255
|
-
content:
|
|
297
|
+
path: 'apps/web/src/lib/auth-client.ts',
|
|
298
|
+
content: authClientTs,
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
path: 'apps/web/src/api/index.ts',
|
|
302
|
+
content: apiIndexTs,
|
|
256
303
|
},
|
|
257
304
|
{
|
|
258
305
|
path: 'apps/web/.gitignore',
|
package/src/init/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { generateModelsPackage } from './generators/models.js';
|
|
|
17
17
|
import { generateMonorepoFiles } from './generators/monorepo.js';
|
|
18
18
|
import { generatePackageJson } from './generators/package.js';
|
|
19
19
|
import { generateSourceFiles } from './generators/source.js';
|
|
20
|
+
import { generateUiPackageFiles } from './generators/ui.js';
|
|
20
21
|
import { generateWebAppFiles } from './generators/web.js';
|
|
21
22
|
import {
|
|
22
23
|
type DeployTarget,
|
|
@@ -294,6 +295,11 @@ export async function initCommand(
|
|
|
294
295
|
// Collect auth app files for fullstack template
|
|
295
296
|
const authAppFiles = isFullstack ? generateAuthAppFiles(templateOptions) : [];
|
|
296
297
|
|
|
298
|
+
// Collect UI package files for fullstack template
|
|
299
|
+
const uiPackageFiles = isFullstack
|
|
300
|
+
? generateUiPackageFiles(templateOptions)
|
|
301
|
+
: [];
|
|
302
|
+
|
|
297
303
|
// Write root files (for monorepo)
|
|
298
304
|
for (const { path, content } of rootFiles) {
|
|
299
305
|
const fullPath = join(targetDir, path);
|
|
@@ -329,6 +335,13 @@ export async function initCommand(
|
|
|
329
335
|
await writeFile(fullPath, content);
|
|
330
336
|
}
|
|
331
337
|
|
|
338
|
+
// Write UI package files (shared components)
|
|
339
|
+
for (const { path, content } of uiPackageFiles) {
|
|
340
|
+
const fullPath = join(targetDir, path);
|
|
341
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
342
|
+
await writeFile(fullPath, content);
|
|
343
|
+
}
|
|
344
|
+
|
|
332
345
|
// Initialize encrypted secrets for development stage
|
|
333
346
|
console.log('🔐 Initializing encrypted secrets...\n');
|
|
334
347
|
const secretServices: ComposeServiceName[] = [];
|
|
@@ -359,6 +372,7 @@ export async function initCommand(
|
|
|
359
372
|
|
|
360
373
|
// Auth service secrets (better-auth)
|
|
361
374
|
customSecrets.AUTH_PORT = '3002';
|
|
375
|
+
customSecrets.AUTH_URL = 'http://localhost:3002'; // For API app to call auth service
|
|
362
376
|
customSecrets.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
363
377
|
customSecrets.BETTER_AUTH_URL = 'http://localhost:3002';
|
|
364
378
|
customSecrets.BETTER_AUTH_TRUSTED_ORIGINS =
|
|
@@ -407,7 +421,9 @@ export async function initCommand(
|
|
|
407
421
|
});
|
|
408
422
|
console.log(' Initialized git repository on branch main');
|
|
409
423
|
} catch {
|
|
410
|
-
console.log(
|
|
424
|
+
console.log(
|
|
425
|
+
' Could not initialize git repository (git may not be installed)',
|
|
426
|
+
);
|
|
411
427
|
}
|
|
412
428
|
|
|
413
429
|
// Print success message with next steps
|
|
@@ -449,7 +465,10 @@ function printNextSteps(
|
|
|
449
465
|
console.log(` │ └── web/ # Next.js frontend`);
|
|
450
466
|
}
|
|
451
467
|
console.log(` ├── packages/`);
|
|
452
|
-
console.log(` │
|
|
468
|
+
console.log(` │ ├── models/ # Shared Zod schemas`);
|
|
469
|
+
if (isFullstackTemplate(options.template)) {
|
|
470
|
+
console.log(` │ └── ui/ # Shared UI components`);
|
|
471
|
+
}
|
|
453
472
|
console.log(` ├── .gkm/secrets/ # Encrypted secrets`);
|
|
454
473
|
console.log(` ├── gkm.config.ts # Workspace config`);
|
|
455
474
|
console.log(` └── turbo.json # Turbo config`);
|
|
@@ -470,6 +489,6 @@ function printNextSteps(
|
|
|
470
489
|
console.log('');
|
|
471
490
|
}
|
|
472
491
|
|
|
473
|
-
console.log('📚 Documentation: https://
|
|
492
|
+
console.log('📚 Documentation: https://geekmidas.github.io/toolbox/');
|
|
474
493
|
console.log('');
|
|
475
494
|
}
|
|
@@ -105,7 +105,22 @@ export const config = envParser
|
|
|
105
105
|
// health endpoint
|
|
106
106
|
{
|
|
107
107
|
path: getRoutePath('health.ts'),
|
|
108
|
-
content:
|
|
108
|
+
content: monorepo
|
|
109
|
+
? `import { z } from 'zod';
|
|
110
|
+
import { publicRouter } from '~/router';
|
|
111
|
+
|
|
112
|
+
export const healthEndpoint = publicRouter
|
|
113
|
+
.get('/health')
|
|
114
|
+
.output(z.object({
|
|
115
|
+
status: z.string(),
|
|
116
|
+
timestamp: z.string(),
|
|
117
|
+
}))
|
|
118
|
+
.handle(async () => ({
|
|
119
|
+
status: 'ok',
|
|
120
|
+
timestamp: new Date().toISOString(),
|
|
121
|
+
}));
|
|
122
|
+
`
|
|
123
|
+
: `import { e } from '@geekmidas/constructs/endpoints';
|
|
109
124
|
import { z } from 'zod';
|
|
110
125
|
|
|
111
126
|
export const healthEndpoint = e
|
|
@@ -163,12 +178,12 @@ export const listUsersEndpoint = e
|
|
|
163
178
|
path: getRoutePath('users/get.ts'),
|
|
164
179
|
content: modelsImport
|
|
165
180
|
? `import { e } from '@geekmidas/constructs/endpoints';
|
|
166
|
-
import {
|
|
181
|
+
import { IdParamsSchema } from '${modelsImport}/common';
|
|
167
182
|
import { UserResponseSchema } from '${modelsImport}/user';
|
|
168
183
|
|
|
169
184
|
export const getUserEndpoint = e
|
|
170
185
|
.get('/users/:id')
|
|
171
|
-
.params(
|
|
186
|
+
.params(IdParamsSchema)
|
|
172
187
|
.output(UserResponseSchema)
|
|
173
188
|
.handle(async ({ params }) => ({
|
|
174
189
|
id: params.id,
|
|
@@ -196,6 +211,97 @@ export const getUserEndpoint = e
|
|
|
196
211
|
},
|
|
197
212
|
];
|
|
198
213
|
|
|
214
|
+
// Add auth service for monorepo (calls auth app for session)
|
|
215
|
+
if (options.monorepo) {
|
|
216
|
+
files.push({
|
|
217
|
+
path: 'src/services/auth.ts',
|
|
218
|
+
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
219
|
+
|
|
220
|
+
export interface Session {
|
|
221
|
+
user: {
|
|
222
|
+
id: string;
|
|
223
|
+
email: string;
|
|
224
|
+
name: string;
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface AuthClient {
|
|
229
|
+
getSession: (cookie: string) => Promise<Session | null>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const authService = {
|
|
233
|
+
serviceName: 'auth' as const,
|
|
234
|
+
async register({ envParser, context }: ServiceRegisterOptions) {
|
|
235
|
+
const logger = context.getLogger();
|
|
236
|
+
|
|
237
|
+
const config = envParser
|
|
238
|
+
.create((get) => ({
|
|
239
|
+
url: get('AUTH_URL').string(),
|
|
240
|
+
}))
|
|
241
|
+
.parse();
|
|
242
|
+
|
|
243
|
+
logger.info({ authUrl: config.url }, 'Auth service configured');
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
getSession: async (cookie: string): Promise<Session | null> => {
|
|
247
|
+
const res = await fetch(\`\${config.url}/api/auth/get-session\`, {
|
|
248
|
+
headers: { cookie },
|
|
249
|
+
});
|
|
250
|
+
if (!res.ok) return null;
|
|
251
|
+
return res.json();
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
} satisfies Service<'auth', AuthClient>;
|
|
256
|
+
`,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Add router with session
|
|
260
|
+
files.push({
|
|
261
|
+
path: 'src/router.ts',
|
|
262
|
+
content: `import { e } from '@geekmidas/constructs/endpoints';
|
|
263
|
+
import { UnauthorizedError } from '@geekmidas/errors';
|
|
264
|
+
import { authService, type Session } from './services/auth.js';
|
|
265
|
+
import { logger } from './config/logger.js';
|
|
266
|
+
|
|
267
|
+
// Public router - no auth required
|
|
268
|
+
export const publicRouter = e.logger(logger);
|
|
269
|
+
|
|
270
|
+
// Router with auth service available (but session not enforced)
|
|
271
|
+
export const r = publicRouter.services([authService]);
|
|
272
|
+
|
|
273
|
+
// Session router - requires active session, throws if not authenticated
|
|
274
|
+
export const sessionRouter = r.session<Session>(async ({ services, header }) => {
|
|
275
|
+
const cookie = header('cookie') || '';
|
|
276
|
+
const session = await services.auth.getSession(cookie);
|
|
277
|
+
|
|
278
|
+
if (!session?.user) {
|
|
279
|
+
throw new UnauthorizedError('No active session');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return session;
|
|
283
|
+
});
|
|
284
|
+
`,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Add protected endpoint example
|
|
288
|
+
files.push({
|
|
289
|
+
path: getRoutePath('profile.ts'),
|
|
290
|
+
content: `import { z } from 'zod';
|
|
291
|
+
import { sessionRouter } from '~/router';
|
|
292
|
+
|
|
293
|
+
export const profileEndpoint = sessionRouter
|
|
294
|
+
.get('/profile')
|
|
295
|
+
.output(z.object({
|
|
296
|
+
id: z.string(),
|
|
297
|
+
email: z.string(),
|
|
298
|
+
name: z.string(),
|
|
299
|
+
}))
|
|
300
|
+
.handle(async ({ session }) => session.user);
|
|
301
|
+
`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
199
305
|
// Add database service if enabled
|
|
200
306
|
if (options.database) {
|
|
201
307
|
files.push({
|
package/src/init/versions.ts
CHANGED
|
@@ -1,58 +1,30 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
// Load package.json - handles both bundled (flat dist/) and source (nested src/init/)
|
|
6
|
-
function loadPackageJson(): { version: string } {
|
|
7
|
-
try {
|
|
8
|
-
// Try flat dist path first (../package.json from dist/)
|
|
9
|
-
return require('../package.json');
|
|
10
|
-
} catch {
|
|
11
|
-
// Fall back to nested source path (../../package.json from src/init/)
|
|
12
|
-
return require('../../package.json');
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const pkg = loadPackageJson();
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* CLI version from package.json (used for scaffolded projects)
|
|
20
|
-
*/
|
|
21
|
-
export const CLI_VERSION = `~${pkg.version}`;
|
|
22
|
-
|
|
23
1
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
2
|
+
* Package versions for @geekmidas packages
|
|
3
|
+
*
|
|
4
|
+
* AUTO-GENERATED - Do not edit manually
|
|
5
|
+
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
27
6
|
*/
|
|
28
7
|
export const GEEKMIDAS_VERSIONS = {
|
|
29
|
-
'@geekmidas/audit': '~0.
|
|
30
|
-
'@geekmidas/auth': '~0.
|
|
31
|
-
'@geekmidas/cache': '~0.
|
|
32
|
-
'@geekmidas/cli':
|
|
33
|
-
'@geekmidas/client': '~0.
|
|
34
|
-
'@geekmidas/cloud': '~0.
|
|
35
|
-
'@geekmidas/constructs': '~0.
|
|
36
|
-
'@geekmidas/db': '~0.
|
|
37
|
-
'@geekmidas/emailkit': '~0.
|
|
38
|
-
'@geekmidas/envkit': '~0.
|
|
39
|
-
'@geekmidas/errors': '~
|
|
40
|
-
'@geekmidas/events': '~0.
|
|
41
|
-
'@geekmidas/logger': '~0.
|
|
42
|
-
'@geekmidas/rate-limit': '~0.
|
|
43
|
-
'@geekmidas/schema': '~
|
|
44
|
-
'@geekmidas/services': '~0.
|
|
45
|
-
'@geekmidas/storage': '~
|
|
46
|
-
'@geekmidas/studio': '~0.
|
|
47
|
-
'@geekmidas/telescope': '~0.
|
|
48
|
-
'@geekmidas/testkit': '~0.
|
|
49
|
-
};
|
|
8
|
+
'@geekmidas/audit': '~1.0.0',
|
|
9
|
+
'@geekmidas/auth': '~1.0.0',
|
|
10
|
+
'@geekmidas/cache': '~1.0.0',
|
|
11
|
+
'@geekmidas/cli': '~1.0.0',
|
|
12
|
+
'@geekmidas/client': '~1.0.0',
|
|
13
|
+
'@geekmidas/cloud': '~1.0.0',
|
|
14
|
+
'@geekmidas/constructs': '~1.0.0',
|
|
15
|
+
'@geekmidas/db': '~1.0.0',
|
|
16
|
+
'@geekmidas/emailkit': '~1.0.0',
|
|
17
|
+
'@geekmidas/envkit': '~1.0.0',
|
|
18
|
+
'@geekmidas/errors': '~1.0.0',
|
|
19
|
+
'@geekmidas/events': '~1.0.0',
|
|
20
|
+
'@geekmidas/logger': '~1.0.0',
|
|
21
|
+
'@geekmidas/rate-limit': '~1.0.0',
|
|
22
|
+
'@geekmidas/schema': '~1.0.0',
|
|
23
|
+
'@geekmidas/services': '~1.0.0',
|
|
24
|
+
'@geekmidas/storage': '~1.0.0',
|
|
25
|
+
'@geekmidas/studio': '~1.0.0',
|
|
26
|
+
'@geekmidas/telescope': '~1.0.0',
|
|
27
|
+
'@geekmidas/testkit': '~1.0.0',
|
|
28
|
+
} as const;
|
|
50
29
|
|
|
51
30
|
export type GeekmidasPackage = keyof typeof GEEKMIDAS_VERSIONS;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get the version for a @geekmidas package
|
|
55
|
-
*/
|
|
56
|
-
export function getPackageVersion(pkg: GeekmidasPackage): string {
|
|
57
|
-
return GEEKMIDAS_VERSIONS[pkg]!;
|
|
58
|
-
}
|