@geekmidas/cli 0.10.0 → 0.13.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/README.md +525 -0
- package/dist/bundler-B1qy9b-j.cjs +112 -0
- package/dist/bundler-B1qy9b-j.cjs.map +1 -0
- package/dist/bundler-DskIqW2t.mjs +111 -0
- package/dist/bundler-DskIqW2t.mjs.map +1 -0
- package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
- package/dist/config-AmInkU7k.cjs.map +1 -0
- package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
- package/dist/config-DYULeEv8.mjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/encryption-C8H-38Yy.mjs +42 -0
- package/dist/encryption-C8H-38Yy.mjs.map +1 -0
- package/dist/encryption-Dyf_r1h-.cjs +44 -0
- package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
- package/dist/index.cjs +2123 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2141 -192
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
- package/dist/openapi-BfFlOBCG.mjs.map +1 -0
- package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
- package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
- package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
- package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
- package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
- package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.d.cts.map +1 -1
- package/dist/openapi-react-query.d.mts.map +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/storage-BOOpAF8N.cjs +5 -0
- package/dist/storage-Bj1E26lU.cjs +187 -0
- package/dist/storage-Bj1E26lU.cjs.map +1 -0
- package/dist/storage-kSxTjkNb.mjs +133 -0
- package/dist/storage-kSxTjkNb.mjs.map +1 -0
- package/dist/storage-tgZSUnKl.mjs +3 -0
- package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
- package/dist/types-BR0M2v_c.d.mts.map +1 -0
- package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
- package/dist/types-BhkZc-vm.d.cts.map +1 -0
- package/examples/cron-example.ts +27 -27
- package/examples/env.ts +27 -27
- package/examples/function-example.ts +31 -31
- package/examples/gkm.config.json +20 -20
- package/examples/gkm.config.ts +8 -8
- package/examples/gkm.minimal.config.json +5 -5
- package/examples/gkm.production.config.json +25 -25
- package/examples/logger.ts +2 -2
- package/package.json +6 -6
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
- package/src/__tests__/config.spec.ts +55 -55
- package/src/__tests__/loadEnvFiles.spec.ts +93 -93
- package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
- package/src/__tests__/openapi-react-query.spec.ts +497 -497
- package/src/__tests__/openapi.spec.ts +428 -428
- package/src/__tests__/test-helpers.ts +76 -76
- package/src/auth/__tests__/credentials.spec.ts +204 -0
- package/src/auth/__tests__/index.spec.ts +168 -0
- package/src/auth/credentials.ts +187 -0
- package/src/auth/index.ts +226 -0
- package/src/build/__tests__/bundler.spec.ts +444 -0
- package/src/build/__tests__/index-new.spec.ts +474 -474
- package/src/build/__tests__/manifests.spec.ts +333 -333
- package/src/build/bundler.ts +210 -0
- package/src/build/endpoint-analyzer.ts +236 -0
- package/src/build/handler-templates.ts +1253 -0
- package/src/build/index.ts +260 -179
- package/src/build/manifests.ts +52 -52
- package/src/build/providerResolver.ts +145 -145
- package/src/build/types.ts +64 -43
- package/src/config.ts +39 -39
- package/src/deploy/__tests__/docker.spec.ts +111 -0
- package/src/deploy/__tests__/dokploy.spec.ts +245 -0
- package/src/deploy/__tests__/init.spec.ts +662 -0
- package/src/deploy/docker.ts +128 -0
- package/src/deploy/dokploy.ts +204 -0
- package/src/deploy/index.ts +136 -0
- package/src/deploy/init.ts +484 -0
- package/src/deploy/types.ts +48 -0
- package/src/dev/__tests__/index.spec.ts +266 -266
- package/src/dev/index.ts +647 -601
- package/src/docker/__tests__/compose.spec.ts +531 -0
- package/src/docker/__tests__/templates.spec.ts +280 -0
- package/src/docker/compose.ts +273 -0
- package/src/docker/index.ts +230 -0
- package/src/docker/templates.ts +446 -0
- package/src/generators/CronGenerator.ts +72 -72
- package/src/generators/EndpointGenerator.ts +699 -398
- package/src/generators/FunctionGenerator.ts +84 -84
- package/src/generators/Generator.ts +72 -72
- package/src/generators/OpenApiTsGenerator.ts +577 -577
- package/src/generators/SubscriberGenerator.ts +124 -124
- package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
- package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
- package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
- package/src/generators/index.ts +4 -4
- package/src/index.ts +623 -201
- package/src/init/__tests__/generators.spec.ts +334 -334
- package/src/init/__tests__/init.spec.ts +332 -332
- package/src/init/__tests__/utils.spec.ts +89 -89
- package/src/init/generators/config.ts +175 -175
- package/src/init/generators/docker.ts +41 -41
- package/src/init/generators/env.ts +72 -72
- package/src/init/generators/index.ts +1 -1
- package/src/init/generators/models.ts +64 -64
- package/src/init/generators/monorepo.ts +161 -161
- package/src/init/generators/package.ts +71 -71
- package/src/init/generators/source.ts +6 -6
- package/src/init/index.ts +203 -208
- package/src/init/templates/api.ts +115 -115
- package/src/init/templates/index.ts +75 -75
- package/src/init/templates/minimal.ts +98 -98
- package/src/init/templates/serverless.ts +89 -89
- package/src/init/templates/worker.ts +98 -98
- package/src/init/utils.ts +54 -56
- package/src/openapi-react-query.ts +194 -194
- package/src/openapi.ts +63 -63
- package/src/secrets/__tests__/encryption.spec.ts +226 -0
- package/src/secrets/__tests__/generator.spec.ts +319 -0
- package/src/secrets/__tests__/index.spec.ts +91 -0
- package/src/secrets/__tests__/storage.spec.ts +611 -0
- package/src/secrets/encryption.ts +91 -0
- package/src/secrets/generator.ts +164 -0
- package/src/secrets/index.ts +383 -0
- package/src/secrets/storage.ts +192 -0
- package/src/secrets/types.ts +53 -0
- package/src/types.ts +295 -176
- package/tsdown.config.ts +11 -8
- package/dist/config-BrkUalUh.mjs.map +0 -1
- package/dist/config-C9aXOHBe.cjs.map +0 -1
- package/dist/openapi-BeHLKcwP.cjs.map +0 -1
- package/dist/openapi-CZLI4QTr.mjs.map +0 -1
- package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
- package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
- package/dist/types-DXgiA1sF.d.mts.map +0 -1
- package/dist/types-b-vwGpqc.d.cts.map +0 -1
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { getDokployCredentials, getDokployToken } from '../auth';
|
|
5
|
+
import type { DokployDeployConfig } from './types';
|
|
6
|
+
|
|
7
|
+
const logger = console;
|
|
8
|
+
|
|
9
|
+
export interface DeployInitOptions {
|
|
10
|
+
/** Dokploy endpoint URL (optional if logged in) */
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
/** Project name (creates new or uses existing) */
|
|
13
|
+
projectName: string;
|
|
14
|
+
/** Application name */
|
|
15
|
+
appName: string;
|
|
16
|
+
/** Use existing project ID instead of creating/finding */
|
|
17
|
+
projectId?: string;
|
|
18
|
+
/** Registry ID in Dokploy (optional) */
|
|
19
|
+
registryId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface DokployProject {
|
|
23
|
+
projectId: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string | null;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
adminId: string;
|
|
28
|
+
environments?: Array<{
|
|
29
|
+
environmentId: string;
|
|
30
|
+
name: string;
|
|
31
|
+
description: string | null;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface DokployApplication {
|
|
36
|
+
applicationId: string;
|
|
37
|
+
name: string;
|
|
38
|
+
appName: string;
|
|
39
|
+
projectId: string;
|
|
40
|
+
environmentId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface DokployRegistry {
|
|
44
|
+
registryId: string;
|
|
45
|
+
registryName: string;
|
|
46
|
+
registryUrl: string;
|
|
47
|
+
username: string;
|
|
48
|
+
imagePrefix: string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the Dokploy API token from stored credentials or environment
|
|
53
|
+
*/
|
|
54
|
+
async function getApiToken(): Promise<string> {
|
|
55
|
+
const token = await getDokployToken();
|
|
56
|
+
if (!token) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
'Dokploy credentials not found.\n' +
|
|
59
|
+
'Run "gkm login --service dokploy" to authenticate, or set DOKPLOY_API_TOKEN.',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return token;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Make a request to the Dokploy API
|
|
67
|
+
*/
|
|
68
|
+
async function dokployRequest<T>(
|
|
69
|
+
method: 'GET' | 'POST',
|
|
70
|
+
endpoint: string,
|
|
71
|
+
baseUrl: string,
|
|
72
|
+
token: string,
|
|
73
|
+
body?: Record<string, unknown>,
|
|
74
|
+
): Promise<T> {
|
|
75
|
+
const url = `${baseUrl}/api/${endpoint}`;
|
|
76
|
+
|
|
77
|
+
const response = await fetch(url, {
|
|
78
|
+
method,
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
Authorization: `Bearer ${token}`,
|
|
82
|
+
},
|
|
83
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
let errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const errorBody = (await response.json()) as { message?: string };
|
|
91
|
+
if (errorBody.message) {
|
|
92
|
+
errorMessage = `Dokploy API error: ${errorBody.message}`;
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
// Ignore JSON parse errors
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw new Error(errorMessage);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Handle empty responses
|
|
102
|
+
const text = await response.text();
|
|
103
|
+
if (!text) {
|
|
104
|
+
return {} as T;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return JSON.parse(text) as T;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get all projects from Dokploy
|
|
112
|
+
*/
|
|
113
|
+
async function getProjects(
|
|
114
|
+
baseUrl: string,
|
|
115
|
+
token: string,
|
|
116
|
+
): Promise<DokployProject[]> {
|
|
117
|
+
return dokployRequest<DokployProject[]>('GET', 'project.all', baseUrl, token);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create a new project in Dokploy
|
|
122
|
+
*/
|
|
123
|
+
async function createProject(
|
|
124
|
+
baseUrl: string,
|
|
125
|
+
token: string,
|
|
126
|
+
name: string,
|
|
127
|
+
description?: string,
|
|
128
|
+
): Promise<DokployProject> {
|
|
129
|
+
return dokployRequest<DokployProject>(
|
|
130
|
+
'POST',
|
|
131
|
+
'project.create',
|
|
132
|
+
baseUrl,
|
|
133
|
+
token,
|
|
134
|
+
{
|
|
135
|
+
name,
|
|
136
|
+
description: description || `Created by gkm CLI`,
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get project by ID to get environment info
|
|
143
|
+
*/
|
|
144
|
+
async function getProject(
|
|
145
|
+
baseUrl: string,
|
|
146
|
+
token: string,
|
|
147
|
+
projectId: string,
|
|
148
|
+
): Promise<DokployProject> {
|
|
149
|
+
return dokployRequest<DokployProject>('POST', 'project.one', baseUrl, token, {
|
|
150
|
+
projectId,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Create a new application in Dokploy
|
|
156
|
+
*/
|
|
157
|
+
async function createApplication(
|
|
158
|
+
baseUrl: string,
|
|
159
|
+
token: string,
|
|
160
|
+
name: string,
|
|
161
|
+
projectId: string,
|
|
162
|
+
): Promise<DokployApplication> {
|
|
163
|
+
// First get the project to find its environment ID
|
|
164
|
+
const project = await getProject(baseUrl, token, projectId);
|
|
165
|
+
|
|
166
|
+
// Use the first environment or create one
|
|
167
|
+
let environmentId: string;
|
|
168
|
+
|
|
169
|
+
const firstEnv = project.environments?.[0];
|
|
170
|
+
if (firstEnv) {
|
|
171
|
+
environmentId = firstEnv.environmentId;
|
|
172
|
+
} else {
|
|
173
|
+
// Create a default environment
|
|
174
|
+
const env = await dokployRequest<{ environmentId: string }>(
|
|
175
|
+
'POST',
|
|
176
|
+
'environment.create',
|
|
177
|
+
baseUrl,
|
|
178
|
+
token,
|
|
179
|
+
{
|
|
180
|
+
projectId,
|
|
181
|
+
name: 'production',
|
|
182
|
+
description: 'Production environment',
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
environmentId = env.environmentId;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return dokployRequest<DokployApplication>(
|
|
189
|
+
'POST',
|
|
190
|
+
'application.create',
|
|
191
|
+
baseUrl,
|
|
192
|
+
token,
|
|
193
|
+
{
|
|
194
|
+
name,
|
|
195
|
+
projectId,
|
|
196
|
+
environmentId,
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Configure application for Docker registry deployment
|
|
203
|
+
*/
|
|
204
|
+
async function configureApplicationRegistry(
|
|
205
|
+
baseUrl: string,
|
|
206
|
+
token: string,
|
|
207
|
+
applicationId: string,
|
|
208
|
+
registryId: string,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
await dokployRequest('POST', 'application.update', baseUrl, token, {
|
|
211
|
+
applicationId,
|
|
212
|
+
registryId,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get available registries
|
|
218
|
+
*/
|
|
219
|
+
async function getRegistries(
|
|
220
|
+
baseUrl: string,
|
|
221
|
+
token: string,
|
|
222
|
+
): Promise<DokployRegistry[]> {
|
|
223
|
+
return dokployRequest<DokployRegistry[]>(
|
|
224
|
+
'GET',
|
|
225
|
+
'registry.all',
|
|
226
|
+
baseUrl,
|
|
227
|
+
token,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Update gkm.config.ts with Dokploy configuration
|
|
233
|
+
*/
|
|
234
|
+
export async function updateConfig(
|
|
235
|
+
config: DokployDeployConfig,
|
|
236
|
+
cwd: string = process.cwd(),
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
const configPath = join(cwd, 'gkm.config.ts');
|
|
239
|
+
|
|
240
|
+
if (!existsSync(configPath)) {
|
|
241
|
+
logger.warn(
|
|
242
|
+
'\n gkm.config.ts not found. Add this configuration manually:\n',
|
|
243
|
+
);
|
|
244
|
+
logger.log(` providers: {`);
|
|
245
|
+
logger.log(` dokploy: {`);
|
|
246
|
+
logger.log(` endpoint: '${config.endpoint}',`);
|
|
247
|
+
logger.log(` projectId: '${config.projectId}',`);
|
|
248
|
+
logger.log(` applicationId: '${config.applicationId}',`);
|
|
249
|
+
logger.log(` },`);
|
|
250
|
+
logger.log(` },`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const content = await readFile(configPath, 'utf-8');
|
|
255
|
+
|
|
256
|
+
// Check if providers.dokploy already exists
|
|
257
|
+
if (content.includes('dokploy:') && content.includes('applicationId:')) {
|
|
258
|
+
logger.log('\n Dokploy config already exists in gkm.config.ts');
|
|
259
|
+
logger.log(' Updating with new values...');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Try to add or update the dokploy config
|
|
263
|
+
let newContent: string;
|
|
264
|
+
|
|
265
|
+
if (content.includes('providers:')) {
|
|
266
|
+
// Add dokploy to existing providers
|
|
267
|
+
if (content.includes('dokploy:')) {
|
|
268
|
+
// Update existing dokploy config
|
|
269
|
+
newContent = content.replace(
|
|
270
|
+
/dokploy:\s*\{[^}]*\}/,
|
|
271
|
+
`dokploy: {
|
|
272
|
+
endpoint: '${config.endpoint}',
|
|
273
|
+
projectId: '${config.projectId}',
|
|
274
|
+
applicationId: '${config.applicationId}',
|
|
275
|
+
}`,
|
|
276
|
+
);
|
|
277
|
+
} else {
|
|
278
|
+
// Add dokploy to providers
|
|
279
|
+
newContent = content.replace(
|
|
280
|
+
/providers:\s*\{/,
|
|
281
|
+
`providers: {
|
|
282
|
+
dokploy: {
|
|
283
|
+
endpoint: '${config.endpoint}',
|
|
284
|
+
projectId: '${config.projectId}',
|
|
285
|
+
applicationId: '${config.applicationId}',
|
|
286
|
+
},`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
// Add providers section before the closing of defineConfig
|
|
291
|
+
newContent = content.replace(
|
|
292
|
+
/}\s*\)\s*;?\s*$/,
|
|
293
|
+
`
|
|
294
|
+
providers: {
|
|
295
|
+
dokploy: {
|
|
296
|
+
endpoint: '${config.endpoint}',
|
|
297
|
+
projectId: '${config.projectId}',
|
|
298
|
+
applicationId: '${config.applicationId}',
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});`,
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
await writeFile(configPath, newContent);
|
|
306
|
+
logger.log('\n ✓ Updated gkm.config.ts with Dokploy configuration');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Initialize Dokploy deployment configuration
|
|
311
|
+
*/
|
|
312
|
+
export async function deployInitCommand(
|
|
313
|
+
options: DeployInitOptions,
|
|
314
|
+
): Promise<DokployDeployConfig> {
|
|
315
|
+
const {
|
|
316
|
+
projectName,
|
|
317
|
+
appName,
|
|
318
|
+
projectId: existingProjectId,
|
|
319
|
+
registryId,
|
|
320
|
+
} = options;
|
|
321
|
+
|
|
322
|
+
// Get endpoint from options or stored credentials
|
|
323
|
+
let endpoint = options.endpoint;
|
|
324
|
+
if (!endpoint) {
|
|
325
|
+
const stored = await getDokployCredentials();
|
|
326
|
+
if (stored) {
|
|
327
|
+
endpoint = stored.endpoint;
|
|
328
|
+
} else {
|
|
329
|
+
throw new Error(
|
|
330
|
+
'Dokploy endpoint not specified.\n' +
|
|
331
|
+
'Either run "gkm login --service dokploy" first, or provide --endpoint.',
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
logger.log(`\n🚀 Initializing Dokploy deployment...`);
|
|
337
|
+
logger.log(` Endpoint: ${endpoint}`);
|
|
338
|
+
|
|
339
|
+
const token = await getApiToken();
|
|
340
|
+
|
|
341
|
+
// Step 1: Find or create project
|
|
342
|
+
let projectId: string;
|
|
343
|
+
|
|
344
|
+
if (existingProjectId) {
|
|
345
|
+
projectId = existingProjectId;
|
|
346
|
+
logger.log(`\n📁 Using existing project: ${projectId}`);
|
|
347
|
+
} else {
|
|
348
|
+
logger.log(`\n📁 Looking for project: ${projectName}`);
|
|
349
|
+
|
|
350
|
+
const projects = await getProjects(endpoint, token);
|
|
351
|
+
const existingProject = projects.find(
|
|
352
|
+
(p) => p.name.toLowerCase() === projectName.toLowerCase(),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
if (existingProject) {
|
|
356
|
+
projectId = existingProject.projectId;
|
|
357
|
+
logger.log(` Found existing project: ${projectId}`);
|
|
358
|
+
} else {
|
|
359
|
+
logger.log(` Creating new project...`);
|
|
360
|
+
const project = await createProject(endpoint, token, projectName);
|
|
361
|
+
projectId = project.projectId;
|
|
362
|
+
logger.log(` ✓ Created project: ${projectId}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Step 2: Create application
|
|
367
|
+
logger.log(`\n📦 Creating application: ${appName}`);
|
|
368
|
+
const application = await createApplication(
|
|
369
|
+
endpoint,
|
|
370
|
+
token,
|
|
371
|
+
appName,
|
|
372
|
+
projectId,
|
|
373
|
+
);
|
|
374
|
+
logger.log(` ✓ Created application: ${application.applicationId}`);
|
|
375
|
+
|
|
376
|
+
// Step 3: Configure registry if provided
|
|
377
|
+
if (registryId) {
|
|
378
|
+
logger.log(`\n🔧 Configuring registry: ${registryId}`);
|
|
379
|
+
await configureApplicationRegistry(
|
|
380
|
+
endpoint,
|
|
381
|
+
token,
|
|
382
|
+
application.applicationId,
|
|
383
|
+
registryId,
|
|
384
|
+
);
|
|
385
|
+
logger.log(` ✓ Registry configured`);
|
|
386
|
+
} else {
|
|
387
|
+
// List available registries
|
|
388
|
+
try {
|
|
389
|
+
const registries = await getRegistries(endpoint, token);
|
|
390
|
+
if (registries.length > 0) {
|
|
391
|
+
logger.log(`\n📋 Available registries:`);
|
|
392
|
+
for (const reg of registries) {
|
|
393
|
+
logger.log(
|
|
394
|
+
` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
logger.log(`\n To use a registry, run with --registry-id <id>`);
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
// Ignore registry listing errors
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Step 4: Build config
|
|
405
|
+
const config: DokployDeployConfig = {
|
|
406
|
+
endpoint,
|
|
407
|
+
projectId,
|
|
408
|
+
applicationId: application.applicationId,
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// Step 5: Update gkm.config.ts
|
|
412
|
+
await updateConfig(config);
|
|
413
|
+
|
|
414
|
+
logger.log(`\n✅ Dokploy deployment initialized!`);
|
|
415
|
+
logger.log(`\n📋 Configuration:`);
|
|
416
|
+
logger.log(` Project ID: ${projectId}`);
|
|
417
|
+
logger.log(` Application ID: ${application.applicationId}`);
|
|
418
|
+
logger.log(`\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);
|
|
419
|
+
logger.log(`\n📝 Next steps:`);
|
|
420
|
+
logger.log(` 1. Initialize secrets: gkm secrets:init --stage production`);
|
|
421
|
+
logger.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);
|
|
422
|
+
|
|
423
|
+
return config;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* List available Dokploy resources
|
|
428
|
+
*/
|
|
429
|
+
export async function deployListCommand(options: {
|
|
430
|
+
endpoint?: string;
|
|
431
|
+
resource: 'projects' | 'registries';
|
|
432
|
+
}): Promise<void> {
|
|
433
|
+
// Get endpoint from options or stored credentials
|
|
434
|
+
let endpoint = options.endpoint;
|
|
435
|
+
if (!endpoint) {
|
|
436
|
+
const stored = await getDokployCredentials();
|
|
437
|
+
if (stored) {
|
|
438
|
+
endpoint = stored.endpoint;
|
|
439
|
+
} else {
|
|
440
|
+
throw new Error(
|
|
441
|
+
'Dokploy endpoint not specified.\n' +
|
|
442
|
+
'Either run "gkm login --service dokploy" first, or provide --endpoint.',
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const { resource } = options;
|
|
448
|
+
const token = await getApiToken();
|
|
449
|
+
|
|
450
|
+
if (resource === 'projects') {
|
|
451
|
+
logger.log(`\n📁 Projects in ${endpoint}:`);
|
|
452
|
+
const projects = await getProjects(endpoint, token);
|
|
453
|
+
|
|
454
|
+
if (projects.length === 0) {
|
|
455
|
+
logger.log(' No projects found');
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for (const project of projects) {
|
|
460
|
+
logger.log(`\n ${project.name} (${project.projectId})`);
|
|
461
|
+
if (project.description) {
|
|
462
|
+
logger.log(` ${project.description}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
} else if (resource === 'registries') {
|
|
466
|
+
logger.log(`\n🐳 Registries in ${endpoint}:`);
|
|
467
|
+
const registries = await getRegistries(endpoint, token);
|
|
468
|
+
|
|
469
|
+
if (registries.length === 0) {
|
|
470
|
+
logger.log(' No registries configured');
|
|
471
|
+
logger.log(' Add a registry in Dokploy: Settings > Docker Registry');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
for (const registry of registries) {
|
|
476
|
+
logger.log(`\n ${registry.registryName} (${registry.registryId})`);
|
|
477
|
+
logger.log(` URL: ${registry.registryUrl}`);
|
|
478
|
+
logger.log(` Username: ${registry.username}`);
|
|
479
|
+
if (registry.imagePrefix) {
|
|
480
|
+
logger.log(` Prefix: ${registry.imagePrefix}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Supported deploy providers */
|
|
2
|
+
export type DeployProvider = 'docker' | 'dokploy' | 'aws-lambda';
|
|
3
|
+
|
|
4
|
+
/** Options for the deploy command */
|
|
5
|
+
export interface DeployOptions {
|
|
6
|
+
/** Deploy provider */
|
|
7
|
+
provider: DeployProvider;
|
|
8
|
+
/** Deployment stage (e.g., 'production', 'staging') */
|
|
9
|
+
stage: string;
|
|
10
|
+
/** Image tag (default: stage-timestamp) */
|
|
11
|
+
tag?: string;
|
|
12
|
+
/** Skip pushing image to registry */
|
|
13
|
+
skipPush?: boolean;
|
|
14
|
+
/** Skip building (use existing build) */
|
|
15
|
+
skipBuild?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Result from a deployment */
|
|
19
|
+
export interface DeployResult {
|
|
20
|
+
/** Docker image reference (if applicable) */
|
|
21
|
+
imageRef?: string;
|
|
22
|
+
/** Ephemeral master key for GKM_MASTER_KEY */
|
|
23
|
+
masterKey?: string;
|
|
24
|
+
/** Deployment ID (for Dokploy) */
|
|
25
|
+
deploymentId?: string;
|
|
26
|
+
/** Deployment URL (if available) */
|
|
27
|
+
url?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Docker provider configuration */
|
|
31
|
+
export interface DockerDeployConfig {
|
|
32
|
+
/** Container registry URL */
|
|
33
|
+
registry?: string;
|
|
34
|
+
/** Image name (default: from package.json) */
|
|
35
|
+
imageName?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Dokploy provider configuration */
|
|
39
|
+
export interface DokployDeployConfig {
|
|
40
|
+
/** Dokploy API endpoint */
|
|
41
|
+
endpoint: string;
|
|
42
|
+
/** Project ID in Dokploy */
|
|
43
|
+
projectId: string;
|
|
44
|
+
/** Application ID in Dokploy */
|
|
45
|
+
applicationId: string;
|
|
46
|
+
/** Container registry (inherits from docker if not set) */
|
|
47
|
+
registry?: string;
|
|
48
|
+
}
|