@geekmidas/cli 0.12.0 → 0.14.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.
Files changed (82) hide show
  1. package/dist/bundler-BjholBlA.cjs +131 -0
  2. package/dist/bundler-BjholBlA.cjs.map +1 -0
  3. package/dist/bundler-DWctKN1z.mjs +130 -0
  4. package/dist/bundler-DWctKN1z.mjs.map +1 -0
  5. package/dist/config.d.cts +1 -1
  6. package/dist/config.d.mts +1 -1
  7. package/dist/dokploy-api-B7KxOQr3.cjs +3 -0
  8. package/dist/dokploy-api-C7F9VykY.cjs +317 -0
  9. package/dist/dokploy-api-C7F9VykY.cjs.map +1 -0
  10. package/dist/dokploy-api-CaETb2L6.mjs +305 -0
  11. package/dist/dokploy-api-CaETb2L6.mjs.map +1 -0
  12. package/dist/dokploy-api-DHvfmWbi.mjs +3 -0
  13. package/dist/{encryption-Dyf_r1h-.cjs → encryption-D7Efcdi9.cjs} +1 -1
  14. package/dist/{encryption-Dyf_r1h-.cjs.map → encryption-D7Efcdi9.cjs.map} +1 -1
  15. package/dist/{encryption-C8H-38Yy.mjs → encryption-h4Nb6W-M.mjs} +1 -1
  16. package/dist/{encryption-C8H-38Yy.mjs.map → encryption-h4Nb6W-M.mjs.map} +1 -1
  17. package/dist/index.cjs +1520 -1136
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.mjs +1520 -1136
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/{openapi-Bt_1FDpT.cjs → openapi-C89hhkZC.cjs} +3 -3
  22. package/dist/{openapi-Bt_1FDpT.cjs.map → openapi-C89hhkZC.cjs.map} +1 -1
  23. package/dist/{openapi-BfFlOBCG.mjs → openapi-CZVcfxk-.mjs} +3 -3
  24. package/dist/{openapi-BfFlOBCG.mjs.map → openapi-CZVcfxk-.mjs.map} +1 -1
  25. package/dist/{openapi-react-query-B6XTeGqS.mjs → openapi-react-query-CM2_qlW9.mjs} +1 -1
  26. package/dist/{openapi-react-query-B6XTeGqS.mjs.map → openapi-react-query-CM2_qlW9.mjs.map} +1 -1
  27. package/dist/{openapi-react-query-B-sNWHFU.cjs → openapi-react-query-iKjfLzff.cjs} +1 -1
  28. package/dist/{openapi-react-query-B-sNWHFU.cjs.map → openapi-react-query-iKjfLzff.cjs.map} +1 -1
  29. package/dist/openapi-react-query.cjs +1 -1
  30. package/dist/openapi-react-query.mjs +1 -1
  31. package/dist/openapi.cjs +1 -1
  32. package/dist/openapi.d.cts +1 -1
  33. package/dist/openapi.d.mts +1 -1
  34. package/dist/openapi.mjs +1 -1
  35. package/dist/{storage-C9PU_30f.mjs → storage-BaOP55oq.mjs} +48 -2
  36. package/dist/storage-BaOP55oq.mjs.map +1 -0
  37. package/dist/{storage-BXoJvmv2.cjs → storage-Bn3K9Ccu.cjs} +59 -1
  38. package/dist/storage-Bn3K9Ccu.cjs.map +1 -0
  39. package/dist/storage-UfyTn7Zm.cjs +7 -0
  40. package/dist/storage-nkGIjeXt.mjs +3 -0
  41. package/dist/{types-BR0M2v_c.d.mts → types-BgaMXsUa.d.cts} +3 -1
  42. package/dist/{types-BR0M2v_c.d.mts.map → types-BgaMXsUa.d.cts.map} +1 -1
  43. package/dist/{types-BhkZc-vm.d.cts → types-iFk5ms7y.d.mts} +3 -1
  44. package/dist/{types-BhkZc-vm.d.cts.map → types-iFk5ms7y.d.mts.map} +1 -1
  45. package/package.json +4 -4
  46. package/src/auth/__tests__/credentials.spec.ts +127 -0
  47. package/src/auth/__tests__/index.spec.ts +69 -0
  48. package/src/auth/credentials.ts +33 -0
  49. package/src/auth/index.ts +57 -50
  50. package/src/build/__tests__/bundler.spec.ts +444 -0
  51. package/src/build/__tests__/endpoint-analyzer.spec.ts +623 -0
  52. package/src/build/__tests__/handler-templates.spec.ts +272 -0
  53. package/src/build/bundler.ts +126 -8
  54. package/src/build/index.ts +31 -0
  55. package/src/build/types.ts +6 -0
  56. package/src/deploy/__tests__/dokploy-api.spec.ts +698 -0
  57. package/src/deploy/__tests__/dokploy.spec.ts +196 -6
  58. package/src/deploy/__tests__/index.spec.ts +339 -0
  59. package/src/deploy/__tests__/init.spec.ts +147 -16
  60. package/src/deploy/docker.ts +32 -3
  61. package/src/deploy/dokploy-api.ts +581 -0
  62. package/src/deploy/dokploy.ts +66 -93
  63. package/src/deploy/index.ts +587 -32
  64. package/src/deploy/init.ts +192 -249
  65. package/src/deploy/types.ts +19 -1
  66. package/src/dev/__tests__/index.spec.ts +95 -0
  67. package/src/docker/__tests__/templates.spec.ts +144 -0
  68. package/src/docker/index.ts +96 -6
  69. package/src/docker/templates.ts +114 -27
  70. package/src/generators/EndpointGenerator.ts +2 -2
  71. package/src/index.ts +34 -13
  72. package/src/secrets/__tests__/storage.spec.ts +208 -0
  73. package/src/secrets/storage.ts +73 -0
  74. package/src/types.ts +2 -0
  75. package/dist/bundler-DRXCw_YR.mjs +0 -70
  76. package/dist/bundler-DRXCw_YR.mjs.map +0 -1
  77. package/dist/bundler-WsEvH_b2.cjs +0 -71
  78. package/dist/bundler-WsEvH_b2.cjs.map +0 -1
  79. package/dist/storage-BUYQJgz7.cjs +0 -4
  80. package/dist/storage-BXoJvmv2.cjs.map +0 -1
  81. package/dist/storage-C9PU_30f.mjs.map +0 -1
  82. package/dist/storage-DLJAYxzJ.mjs +0 -3
@@ -0,0 +1,581 @@
1
+ /**
2
+ * Centralized Dokploy API client
3
+ *
4
+ * Handles authentication, error handling, and provides typed methods for all Dokploy API endpoints.
5
+ */
6
+
7
+ export interface DokployApiOptions {
8
+ /** Dokploy server URL (e.g., https://dokploy.example.com) */
9
+ baseUrl: string;
10
+ /** API token for authentication */
11
+ token: string;
12
+ }
13
+
14
+ export interface DokployErrorResponse {
15
+ message?: string;
16
+ issues?: Array<{ message: string }>;
17
+ }
18
+
19
+ export class DokployApiError extends Error {
20
+ constructor(
21
+ message: string,
22
+ public status: number,
23
+ public statusText: string,
24
+ public issues?: Array<{ message: string }>,
25
+ ) {
26
+ super(message);
27
+ this.name = 'DokployApiError';
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Dokploy API client
33
+ */
34
+ export class DokployApi {
35
+ private baseUrl: string;
36
+ private token: string;
37
+
38
+ constructor(options: DokployApiOptions) {
39
+ this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash
40
+ this.token = options.token;
41
+ }
42
+
43
+ /**
44
+ * Make a GET request to the Dokploy API
45
+ */
46
+ async get<T>(endpoint: string): Promise<T> {
47
+ return this.request<T>('GET', endpoint);
48
+ }
49
+
50
+ /**
51
+ * Make a POST request to the Dokploy API
52
+ */
53
+ async post<T>(endpoint: string, body?: Record<string, unknown>): Promise<T> {
54
+ return this.request<T>('POST', endpoint, body);
55
+ }
56
+
57
+ /**
58
+ * Make a request to the Dokploy API
59
+ */
60
+ private async request<T>(
61
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE',
62
+ endpoint: string,
63
+ body?: Record<string, unknown>,
64
+ ): Promise<T> {
65
+ const url = `${this.baseUrl}/api/${endpoint}`;
66
+
67
+ const response = await fetch(url, {
68
+ method,
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ 'x-api-key': this.token,
72
+ },
73
+ body: body ? JSON.stringify(body) : undefined,
74
+ });
75
+
76
+ if (!response.ok) {
77
+ let errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;
78
+ let issues: Array<{ message: string }> | undefined;
79
+
80
+ try {
81
+ const errorBody = (await response.json()) as DokployErrorResponse;
82
+ if (errorBody.message) {
83
+ errorMessage = `Dokploy API error: ${errorBody.message}`;
84
+ }
85
+ if (errorBody.issues?.length) {
86
+ issues = errorBody.issues;
87
+ errorMessage += `\n Issues: ${errorBody.issues.map((i) => i.message).join(', ')}`;
88
+ }
89
+ } catch {
90
+ // Ignore JSON parse errors
91
+ }
92
+
93
+ throw new DokployApiError(
94
+ errorMessage,
95
+ response.status,
96
+ response.statusText,
97
+ issues,
98
+ );
99
+ }
100
+
101
+ // Handle empty responses (204 No Content or empty body)
102
+ const text = await response.text();
103
+ if (!text || text.trim() === '') {
104
+ return undefined as T;
105
+ }
106
+ return JSON.parse(text) as T;
107
+ }
108
+
109
+ /**
110
+ * Validate the API token by making a test request
111
+ */
112
+ async validateToken(): Promise<boolean> {
113
+ try {
114
+ await this.get('project.all');
115
+ return true;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ // ============================================
122
+ // Project endpoints
123
+ // ============================================
124
+
125
+ /**
126
+ * List all projects
127
+ */
128
+ async listProjects(): Promise<DokployProject[]> {
129
+ return this.get<DokployProject[]>('project.all');
130
+ }
131
+
132
+ /**
133
+ * Get a single project by ID
134
+ */
135
+ async getProject(projectId: string): Promise<DokployProjectDetails> {
136
+ return this.get<DokployProjectDetails>(
137
+ `project.one?projectId=${projectId}`,
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Create a new project
143
+ */
144
+ async createProject(
145
+ name: string,
146
+ description?: string,
147
+ ): Promise<{ project: DokployProject; environment: DokployEnvironment }> {
148
+ return this.post<{
149
+ project: DokployProject;
150
+ environment: DokployEnvironment;
151
+ }>('project.create', {
152
+ name,
153
+ description: description ?? `Created by gkm CLI`,
154
+ });
155
+ }
156
+
157
+ // ============================================
158
+ // Environment endpoints
159
+ // ============================================
160
+
161
+ /**
162
+ * Create an environment in a project
163
+ */
164
+ async createEnvironment(
165
+ projectId: string,
166
+ name: string,
167
+ description?: string,
168
+ ): Promise<DokployEnvironment> {
169
+ return this.post<DokployEnvironment>('environment.create', {
170
+ projectId,
171
+ name,
172
+ description: description ?? `${name} environment`,
173
+ });
174
+ }
175
+
176
+ // ============================================
177
+ // Application endpoints
178
+ // ============================================
179
+
180
+ /**
181
+ * Create a new application
182
+ */
183
+ async createApplication(
184
+ name: string,
185
+ projectId: string,
186
+ environmentId: string,
187
+ ): Promise<DokployApplication> {
188
+ return this.post<DokployApplication>('application.create', {
189
+ name,
190
+ projectId,
191
+ environmentId,
192
+ appName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Update an application
198
+ */
199
+ async updateApplication(
200
+ applicationId: string,
201
+ updates: Partial<DokployApplicationUpdate>,
202
+ ): Promise<void> {
203
+ await this.post('application.update', {
204
+ applicationId,
205
+ ...updates,
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Save environment variables for an application
211
+ */
212
+ async saveApplicationEnv(applicationId: string, env: string): Promise<void> {
213
+ await this.post('application.saveEnvironment', {
214
+ applicationId,
215
+ env,
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Configure application to use Docker provider (pull from registry)
221
+ *
222
+ * For private registries, either:
223
+ * - Use `registryId` if the registry is configured in Dokploy
224
+ * - Or provide `username`, `password`, and `registryUrl` directly
225
+ */
226
+ async saveDockerProvider(
227
+ applicationId: string,
228
+ dockerImage: string,
229
+ options?: {
230
+ /** Registry ID in Dokploy (for pre-configured registries) */
231
+ registryId?: string;
232
+ /** Registry username (for direct auth) */
233
+ username?: string;
234
+ /** Registry password (for direct auth) */
235
+ password?: string;
236
+ /** Registry URL (for direct auth, e.g., ghcr.io) */
237
+ registryUrl?: string;
238
+ },
239
+ ): Promise<void> {
240
+ await this.post('application.saveDockerProvider', {
241
+ applicationId,
242
+ dockerImage,
243
+ ...options,
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Deploy an application
249
+ */
250
+ async deployApplication(applicationId: string): Promise<void> {
251
+ await this.post('application.deploy', { applicationId });
252
+ }
253
+
254
+ // ============================================
255
+ // Registry endpoints
256
+ // ============================================
257
+
258
+ /**
259
+ * List all registries
260
+ */
261
+ async listRegistries(): Promise<DokployRegistry[]> {
262
+ return this.get<DokployRegistry[]>('registry.all');
263
+ }
264
+
265
+ /**
266
+ * Create a new registry
267
+ */
268
+ async createRegistry(
269
+ registryName: string,
270
+ registryUrl: string,
271
+ username: string,
272
+ password: string,
273
+ options?: {
274
+ imagePrefix?: string;
275
+ },
276
+ ): Promise<DokployRegistry> {
277
+ return this.post<DokployRegistry>('registry.create', {
278
+ registryName,
279
+ registryUrl,
280
+ username,
281
+ password,
282
+ imagePrefix: options?.imagePrefix,
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Get a registry by ID
288
+ */
289
+ async getRegistry(registryId: string): Promise<DokployRegistry> {
290
+ return this.get<DokployRegistry>(`registry.one?registryId=${registryId}`);
291
+ }
292
+
293
+ /**
294
+ * Update a registry
295
+ */
296
+ async updateRegistry(
297
+ registryId: string,
298
+ updates: Partial<{
299
+ registryName: string;
300
+ registryUrl: string;
301
+ username: string;
302
+ password: string;
303
+ imagePrefix: string;
304
+ }>,
305
+ ): Promise<void> {
306
+ await this.post('registry.update', { registryId, ...updates });
307
+ }
308
+
309
+ /**
310
+ * Delete a registry
311
+ */
312
+ async deleteRegistry(registryId: string): Promise<void> {
313
+ await this.post('registry.remove', { registryId });
314
+ }
315
+
316
+ // ============================================
317
+ // Postgres endpoints
318
+ // ============================================
319
+
320
+ /**
321
+ * Create a new Postgres database
322
+ */
323
+ async createPostgres(
324
+ name: string,
325
+ projectId: string,
326
+ environmentId: string,
327
+ options?: {
328
+ appName?: string;
329
+ databaseName?: string;
330
+ databaseUser?: string;
331
+ databasePassword?: string;
332
+ dockerImage?: string;
333
+ description?: string;
334
+ },
335
+ ): Promise<DokployPostgres> {
336
+ return this.post<DokployPostgres>('postgres.create', {
337
+ name,
338
+ projectId,
339
+ environmentId,
340
+ appName:
341
+ options?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
342
+ databaseName: options?.databaseName ?? 'app',
343
+ databaseUser: options?.databaseUser ?? 'postgres',
344
+ databasePassword: options?.databasePassword,
345
+ dockerImage: options?.dockerImage ?? 'postgres:16-alpine',
346
+ description: options?.description ?? `Postgres database for ${name}`,
347
+ });
348
+ }
349
+
350
+ /**
351
+ * Get a Postgres database by ID
352
+ */
353
+ async getPostgres(postgresId: string): Promise<DokployPostgres> {
354
+ return this.get<DokployPostgres>(`postgres.one?postgresId=${postgresId}`);
355
+ }
356
+
357
+ /**
358
+ * Deploy a Postgres database
359
+ */
360
+ async deployPostgres(postgresId: string): Promise<void> {
361
+ await this.post('postgres.deploy', { postgresId });
362
+ }
363
+
364
+ /**
365
+ * Save environment variables for Postgres
366
+ */
367
+ async savePostgresEnv(postgresId: string, env: string): Promise<void> {
368
+ await this.post('postgres.saveEnvironment', { postgresId, env });
369
+ }
370
+
371
+ /**
372
+ * Set external port for Postgres (for external access)
373
+ */
374
+ async savePostgresExternalPort(
375
+ postgresId: string,
376
+ externalPort: number | null,
377
+ ): Promise<void> {
378
+ await this.post('postgres.saveExternalPort', { postgresId, externalPort });
379
+ }
380
+
381
+ /**
382
+ * Update Postgres configuration
383
+ */
384
+ async updatePostgres(
385
+ postgresId: string,
386
+ updates: Partial<DokployPostgresUpdate>,
387
+ ): Promise<void> {
388
+ await this.post('postgres.update', { postgresId, ...updates });
389
+ }
390
+
391
+ // ============================================
392
+ // Redis endpoints
393
+ // ============================================
394
+
395
+ /**
396
+ * Create a new Redis instance
397
+ */
398
+ async createRedis(
399
+ name: string,
400
+ projectId: string,
401
+ environmentId: string,
402
+ options?: {
403
+ appName?: string;
404
+ databasePassword?: string;
405
+ dockerImage?: string;
406
+ description?: string;
407
+ },
408
+ ): Promise<DokployRedis> {
409
+ return this.post<DokployRedis>('redis.create', {
410
+ name,
411
+ projectId,
412
+ environmentId,
413
+ appName:
414
+ options?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
415
+ databasePassword: options?.databasePassword,
416
+ dockerImage: options?.dockerImage ?? 'redis:7-alpine',
417
+ description: options?.description ?? `Redis instance for ${name}`,
418
+ });
419
+ }
420
+
421
+ /**
422
+ * Get a Redis instance by ID
423
+ */
424
+ async getRedis(redisId: string): Promise<DokployRedis> {
425
+ return this.get<DokployRedis>(`redis.one?redisId=${redisId}`);
426
+ }
427
+
428
+ /**
429
+ * Deploy a Redis instance
430
+ */
431
+ async deployRedis(redisId: string): Promise<void> {
432
+ await this.post('redis.deploy', { redisId });
433
+ }
434
+
435
+ /**
436
+ * Save environment variables for Redis
437
+ */
438
+ async saveRedisEnv(redisId: string, env: string): Promise<void> {
439
+ await this.post('redis.saveEnvironment', { redisId, env });
440
+ }
441
+
442
+ /**
443
+ * Set external port for Redis (for external access)
444
+ */
445
+ async saveRedisExternalPort(
446
+ redisId: string,
447
+ externalPort: number | null,
448
+ ): Promise<void> {
449
+ await this.post('redis.saveExternalPort', { redisId, externalPort });
450
+ }
451
+
452
+ /**
453
+ * Update Redis configuration
454
+ */
455
+ async updateRedis(
456
+ redisId: string,
457
+ updates: Partial<DokployRedisUpdate>,
458
+ ): Promise<void> {
459
+ await this.post('redis.update', { redisId, ...updates });
460
+ }
461
+ }
462
+
463
+ // ============================================
464
+ // Type definitions for Dokploy API responses
465
+ // ============================================
466
+
467
+ export interface DokployProject {
468
+ projectId: string;
469
+ name: string;
470
+ description: string | null;
471
+ createdAt?: string;
472
+ adminId?: string;
473
+ }
474
+
475
+ export interface DokployEnvironment {
476
+ environmentId: string;
477
+ name: string;
478
+ description: string | null;
479
+ }
480
+
481
+ export interface DokployProjectDetails extends DokployProject {
482
+ environments: DokployEnvironment[];
483
+ }
484
+
485
+ export interface DokployApplication {
486
+ applicationId: string;
487
+ name: string;
488
+ appName: string;
489
+ projectId: string;
490
+ environmentId?: string;
491
+ }
492
+
493
+ export interface DokployApplicationUpdate {
494
+ registryId: string;
495
+ dockerImage: string;
496
+ sourceType: 'docker';
497
+ }
498
+
499
+ export interface DokployRegistry {
500
+ registryId: string;
501
+ registryName: string;
502
+ registryUrl: string;
503
+ username: string;
504
+ imagePrefix: string | null;
505
+ }
506
+
507
+ export interface DokployPostgres {
508
+ postgresId: string;
509
+ name: string;
510
+ appName: string;
511
+ databaseName: string;
512
+ databaseUser: string;
513
+ databasePassword: string;
514
+ dockerImage: string;
515
+ description: string | null;
516
+ projectId: string;
517
+ environmentId: string;
518
+ applicationStatus: 'idle' | 'running' | 'done' | 'error';
519
+ externalPort: number | null;
520
+ createdAt?: string;
521
+ }
522
+
523
+ export interface DokployPostgresUpdate {
524
+ name: string;
525
+ appName: string;
526
+ databaseName: string;
527
+ databaseUser: string;
528
+ databasePassword: string;
529
+ dockerImage: string;
530
+ description: string;
531
+ }
532
+
533
+ export interface DokployRedis {
534
+ redisId: string;
535
+ name: string;
536
+ appName: string;
537
+ databasePassword: string;
538
+ dockerImage: string;
539
+ description: string | null;
540
+ projectId: string;
541
+ environmentId: string;
542
+ applicationStatus: 'idle' | 'running' | 'done' | 'error';
543
+ externalPort: number | null;
544
+ createdAt?: string;
545
+ }
546
+
547
+ export interface DokployRedisUpdate {
548
+ name: string;
549
+ appName: string;
550
+ databasePassword: string;
551
+ dockerImage: string;
552
+ description: string;
553
+ }
554
+
555
+ /**
556
+ * Create a Dokploy API client from stored credentials or environment
557
+ */
558
+ export async function createDokployApi(
559
+ endpoint?: string,
560
+ ): Promise<DokployApi | null> {
561
+ const { getDokployCredentials } = await import('../auth/credentials');
562
+
563
+ // Try environment variable first
564
+ const envToken = process.env.DOKPLOY_API_TOKEN;
565
+ const envEndpoint = endpoint || process.env.DOKPLOY_ENDPOINT;
566
+
567
+ if (envToken && envEndpoint) {
568
+ return new DokployApi({ baseUrl: envEndpoint, token: envToken });
569
+ }
570
+
571
+ // Fall back to stored credentials
572
+ const creds = await getDokployCredentials();
573
+ if (creds) {
574
+ return new DokployApi({
575
+ baseUrl: endpoint || creds.endpoint,
576
+ token: creds.token,
577
+ });
578
+ }
579
+
580
+ return null;
581
+ }