@mks2508/coolify-mks-cli-mcp 0.4.3 → 0.6.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/cli/coolify-state.d.ts +51 -0
- package/dist/cli/coolify-state.d.ts.map +1 -0
- package/dist/cli/index.js +2862 -631
- package/dist/coolify/config.d.ts +1 -1
- package/dist/coolify/config.d.ts.map +1 -1
- package/dist/coolify/index.d.ts +626 -12
- package/dist/coolify/index.d.ts.map +1 -1
- package/dist/coolify/types.d.ts +87 -3
- package/dist/coolify/types.d.ts.map +1 -1
- package/dist/dist-C4hIkHif.js +66 -0
- package/dist/dist-C4hIkHif.js.map +1 -0
- package/dist/dist-DEPvJhbP.js +3 -0
- package/dist/index.cjs +8511 -28542
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +32 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8470 -28506
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +75 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/sdk.d.ts +356 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/sse.js +3 -1
- package/dist/server/stdio.d.ts +0 -2
- package/dist/server/stdio.d.ts.map +1 -1
- package/dist/server/stdio.js +3307 -1618
- package/dist/tools/definitions.d.ts +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/handlers.d.ts +6 -7
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/trace.d.ts +71 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/utils/format.d.ts +1 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/package.json +13 -7
- package/src/cli/actions.ts +162 -0
- package/src/cli/commands/active-deployments.ts +24 -0
- package/src/cli/commands/build-logs.ts +58 -0
- package/src/cli/commands/cancel-deploy.ts +35 -0
- package/src/cli/commands/config.ts +53 -47
- package/src/cli/commands/create.ts +74 -53
- package/src/cli/commands/databases.ts +63 -0
- package/src/cli/commands/db.ts +68 -0
- package/src/cli/commands/delete.ts +41 -29
- package/src/cli/commands/deploy.ts +42 -21
- package/src/cli/commands/deployments.ts +41 -31
- package/src/cli/commands/destinations.ts +19 -27
- package/src/cli/commands/diagnose.ts +139 -0
- package/src/cli/commands/env.ts +66 -41
- package/src/cli/commands/environments.ts +36 -32
- package/src/cli/commands/exec.ts +39 -0
- package/src/cli/commands/keys.ts +46 -0
- package/src/cli/commands/list.ts +29 -27
- package/src/cli/commands/logs.ts +33 -18
- package/src/cli/commands/network.ts +145 -0
- package/src/cli/commands/projects.ts +51 -39
- package/src/cli/commands/restart.ts +60 -0
- package/src/cli/commands/server-resources.ts +71 -0
- package/src/cli/commands/servers.ts +23 -23
- package/src/cli/commands/service-logs.ts +59 -0
- package/src/cli/commands/services.ts +63 -0
- package/src/cli/commands/show.ts +72 -41
- package/src/cli/commands/start.ts +60 -0
- package/src/cli/commands/stop.ts +60 -0
- package/src/cli/commands/svc.ts +68 -0
- package/src/cli/commands/teams.ts +60 -0
- package/src/cli/commands/update.ts +73 -49
- package/src/cli/commands/version.ts +37 -0
- package/src/cli/coolify-state.ts +88 -0
- package/src/cli/index.ts +400 -125
- package/src/coolify/config.ts +29 -27
- package/src/coolify/index.ts +2221 -371
- package/src/coolify/types.ts +218 -123
- package/src/index.ts +82 -868
- package/src/network.ts +298 -0
- package/src/sdk.ts +597 -0
- package/src/server/index.ts +13 -0
- package/src/server/sse.ts +33 -25
- package/src/server/stdio.ts +24 -27
- package/src/tools/definitions.ts +893 -264
- package/src/tools/handlers.ts +556 -748
- package/src/tools/index.ts +8 -0
- package/src/trace.ts +116 -0
- package/src/utils/format.ts +36 -33
package/src/coolify/index.ts
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { ok, err, isErr, type Result } from
|
|
10
|
-
import { component } from
|
|
11
|
-
import { loadConfig, type ICoolifyConfig } from
|
|
9
|
+
import { ok, err, isErr, type Result } from "@mks2508/no-throw";
|
|
10
|
+
import { component } from "@mks2508/better-logger";
|
|
11
|
+
import { loadConfig, type ICoolifyConfig } from "./config.js";
|
|
12
12
|
import {
|
|
13
13
|
type ICoolifyAppOptions,
|
|
14
14
|
type ICoolifyAppResult,
|
|
15
15
|
type ICoolifyApplication,
|
|
16
|
+
type ICoolifyDatabase,
|
|
17
|
+
type ICoolifyDatabaseBackup,
|
|
16
18
|
type ICoolifyDeleteResult,
|
|
17
19
|
type ICoolifyDeployment,
|
|
18
20
|
type ICoolifyDeployOptions,
|
|
@@ -21,36 +23,41 @@ import {
|
|
|
21
23
|
type ICoolifyEnvironment,
|
|
22
24
|
type ICoolifyLogs,
|
|
23
25
|
type ICoolifyLogsOptions,
|
|
26
|
+
type ICoolifyPrivateKey,
|
|
24
27
|
type ICoolifyProject,
|
|
25
28
|
type ICoolifyServer,
|
|
29
|
+
type ICoolifyServerDomain,
|
|
30
|
+
type ICoolifyServerResource,
|
|
31
|
+
type ICoolifyService as ICoolifyServiceType,
|
|
26
32
|
type ICoolifyTeam,
|
|
27
33
|
type ICoolifyUpdateOptions,
|
|
34
|
+
type ICoolifyVersion,
|
|
28
35
|
type IProgressCallback,
|
|
29
|
-
} from
|
|
36
|
+
} from "./types.js";
|
|
30
37
|
|
|
31
|
-
const log = component(
|
|
38
|
+
const log = component("CoolifyService");
|
|
32
39
|
|
|
33
40
|
/**
|
|
34
41
|
* Coolify API response type.
|
|
35
42
|
*/
|
|
36
43
|
interface ICoolifyApiResponse<T> {
|
|
37
|
-
data?: T
|
|
38
|
-
error?: string
|
|
39
|
-
status: number
|
|
40
|
-
durationMs?: number
|
|
44
|
+
data?: T;
|
|
45
|
+
error?: string;
|
|
46
|
+
status: number;
|
|
47
|
+
durationMs?: number;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
/**
|
|
44
51
|
* Environment variable from Coolify API.
|
|
45
52
|
*/
|
|
46
53
|
export interface ICoolifyEnvVar {
|
|
47
|
-
uuid: string
|
|
48
|
-
key: string
|
|
49
|
-
value: string
|
|
50
|
-
real_value?: string
|
|
51
|
-
is_buildtime: boolean
|
|
52
|
-
is_runtime: boolean
|
|
53
|
-
is_required: boolean
|
|
54
|
+
uuid: string;
|
|
55
|
+
key: string;
|
|
56
|
+
value: string;
|
|
57
|
+
real_value?: string;
|
|
58
|
+
is_buildtime: boolean;
|
|
59
|
+
is_runtime: boolean;
|
|
60
|
+
is_required: boolean;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
/**
|
|
@@ -72,9 +79,9 @@ export interface ICoolifyEnvVar {
|
|
|
72
79
|
* ```
|
|
73
80
|
*/
|
|
74
81
|
export class CoolifyService {
|
|
75
|
-
private baseUrl: string | undefined
|
|
76
|
-
private token: string | undefined
|
|
77
|
-
private config: ICoolifyConfig = {}
|
|
82
|
+
private baseUrl: string | undefined;
|
|
83
|
+
private token: string | undefined;
|
|
84
|
+
private config: ICoolifyConfig = {};
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
87
|
* Checks if the service is configured with URL and token.
|
|
@@ -82,9 +89,9 @@ export class CoolifyService {
|
|
|
82
89
|
* @returns true if both URL and token are set
|
|
83
90
|
*/
|
|
84
91
|
isConfigured(): boolean {
|
|
85
|
-
const hasUrl = !!this.baseUrl || !!process.env.COOLIFY_URL
|
|
86
|
-
const hasToken = !!this.token || !!process.env.COOLIFY_TOKEN
|
|
87
|
-
return hasUrl && hasToken
|
|
92
|
+
const hasUrl = !!this.baseUrl || !!process.env.COOLIFY_URL;
|
|
93
|
+
const hasToken = !!this.token || !!process.env.COOLIFY_TOKEN;
|
|
94
|
+
return hasUrl && hasToken;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
/**
|
|
@@ -93,31 +100,43 @@ export class CoolifyService {
|
|
|
93
100
|
* @returns Result indicating success or error
|
|
94
101
|
*/
|
|
95
102
|
async init(): Promise<Result<void, Error>> {
|
|
96
|
-
const configResult = await loadConfig()
|
|
103
|
+
const configResult = await loadConfig();
|
|
97
104
|
|
|
98
105
|
if (isErr(configResult)) {
|
|
99
|
-
log.error(
|
|
100
|
-
return err(configResult.error)
|
|
106
|
+
log.error("Failed to load config");
|
|
107
|
+
return err(configResult.error);
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
this.config = configResult.value
|
|
104
|
-
this.baseUrl = this.config.url || process.env.COOLIFY_URL
|
|
105
|
-
this.token = this.config.token || process.env.COOLIFY_TOKEN
|
|
110
|
+
this.config = configResult.value;
|
|
111
|
+
this.baseUrl = this.config.url || process.env.COOLIFY_URL;
|
|
112
|
+
this.token = this.config.token || process.env.COOLIFY_TOKEN;
|
|
106
113
|
|
|
107
114
|
if (!this.baseUrl) {
|
|
108
|
-
log.error(
|
|
109
|
-
log.info(
|
|
110
|
-
|
|
115
|
+
log.error("No Coolify URL configured");
|
|
116
|
+
log.info(
|
|
117
|
+
"Set COOLIFY_URL environment variable or run: coolify-mcp config set url <url>",
|
|
118
|
+
);
|
|
119
|
+
return err(
|
|
120
|
+
new Error(
|
|
121
|
+
"No Coolify URL configured. Set COOLIFY_URL or use config command.",
|
|
122
|
+
),
|
|
123
|
+
);
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
if (!this.token) {
|
|
114
|
-
log.error(
|
|
115
|
-
log.info(
|
|
116
|
-
|
|
127
|
+
log.error("No Coolify token configured");
|
|
128
|
+
log.info(
|
|
129
|
+
"Set COOLIFY_TOKEN environment variable or run: coolify-mcp config set token <token>",
|
|
130
|
+
);
|
|
131
|
+
return err(
|
|
132
|
+
new Error(
|
|
133
|
+
"No Coolify token configured. Set COOLIFY_TOKEN or use config command.",
|
|
134
|
+
),
|
|
135
|
+
);
|
|
117
136
|
}
|
|
118
137
|
|
|
119
|
-
log.debug(
|
|
120
|
-
return ok(undefined)
|
|
138
|
+
log.debug("Coolify connection configured");
|
|
139
|
+
return ok(undefined);
|
|
121
140
|
}
|
|
122
141
|
|
|
123
142
|
/**
|
|
@@ -129,57 +148,70 @@ export class CoolifyService {
|
|
|
129
148
|
*/
|
|
130
149
|
private async request<T>(
|
|
131
150
|
endpoint: string,
|
|
132
|
-
options: RequestInit = {}
|
|
151
|
+
options: RequestInit = {},
|
|
133
152
|
): Promise<ICoolifyApiResponse<T>> {
|
|
134
|
-
const startTime = Date.now()
|
|
153
|
+
const startTime = Date.now();
|
|
135
154
|
|
|
136
155
|
if (!this.baseUrl || !this.token) {
|
|
137
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
error: "Coolify not configured",
|
|
158
|
+
status: 0,
|
|
159
|
+
durationMs: Date.now() - startTime,
|
|
160
|
+
};
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
try {
|
|
141
|
-
const baseUrl = this.baseUrl.replace(/\/+$/,
|
|
142
|
-
const url = `${baseUrl}/api/v1${endpoint}
|
|
164
|
+
const baseUrl = this.baseUrl.replace(/\/+$/, "");
|
|
165
|
+
const url = `${baseUrl}/api/v1${endpoint}`;
|
|
143
166
|
|
|
144
167
|
const response = await fetch(url, {
|
|
145
168
|
...options,
|
|
146
169
|
headers: {
|
|
147
170
|
Authorization: `Bearer ${this.token}`,
|
|
148
|
-
|
|
149
|
-
Accept:
|
|
171
|
+
"Content-Type": "application/json",
|
|
172
|
+
Accept: "application/json",
|
|
150
173
|
...options.headers,
|
|
151
174
|
},
|
|
152
|
-
})
|
|
175
|
+
});
|
|
153
176
|
|
|
154
|
-
const text = await response.text()
|
|
155
|
-
const durationMs = Date.now() - startTime
|
|
156
|
-
let data: T | undefined
|
|
177
|
+
const text = await response.text();
|
|
178
|
+
const durationMs = Date.now() - startTime;
|
|
179
|
+
let data: T | undefined;
|
|
157
180
|
|
|
158
181
|
try {
|
|
159
|
-
data = text ? JSON.parse(text) : undefined
|
|
182
|
+
data = text ? JSON.parse(text) : undefined;
|
|
160
183
|
} catch {
|
|
161
184
|
if (!response.ok) {
|
|
162
|
-
return {
|
|
185
|
+
return {
|
|
186
|
+
error: text || `HTTP ${response.status}`,
|
|
187
|
+
status: response.status,
|
|
188
|
+
durationMs,
|
|
189
|
+
};
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
192
|
|
|
166
193
|
if (!response.ok) {
|
|
167
|
-
const parsed = data as
|
|
168
|
-
|
|
194
|
+
const parsed = data as
|
|
195
|
+
| { message?: string; errors?: Record<string, string[]> }
|
|
196
|
+
| undefined;
|
|
197
|
+
let errorMessage = parsed?.message || `HTTP ${response.status}`;
|
|
169
198
|
// Include validation errors if present (Coolify returns { message, errors: { field: [reasons] } })
|
|
170
199
|
if (parsed?.errors) {
|
|
171
200
|
const details = Object.entries(parsed.errors)
|
|
172
|
-
.map(
|
|
173
|
-
|
|
174
|
-
|
|
201
|
+
.map(
|
|
202
|
+
([field, reasons]) =>
|
|
203
|
+
`${field}: ${Array.isArray(reasons) ? reasons.join(", ") : String(reasons)}`,
|
|
204
|
+
)
|
|
205
|
+
.join("; ");
|
|
206
|
+
errorMessage += ` — ${details}`;
|
|
175
207
|
}
|
|
176
|
-
return { error: errorMessage, status: response.status, durationMs }
|
|
208
|
+
return { error: errorMessage, status: response.status, durationMs };
|
|
177
209
|
}
|
|
178
210
|
|
|
179
|
-
return { data, status: response.status, durationMs }
|
|
211
|
+
return { data, status: response.status, durationMs };
|
|
180
212
|
} catch (error) {
|
|
181
|
-
const message = error instanceof Error ? error.message :
|
|
182
|
-
return { error: message, status: 0, durationMs: Date.now() - startTime }
|
|
213
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
214
|
+
return { error: message, status: 0, durationMs: Date.now() - startTime };
|
|
183
215
|
}
|
|
184
216
|
}
|
|
185
217
|
|
|
@@ -192,61 +224,63 @@ export class CoolifyService {
|
|
|
192
224
|
*/
|
|
193
225
|
async deploy(
|
|
194
226
|
options: ICoolifyDeployOptions,
|
|
195
|
-
onProgress?: IProgressCallback
|
|
227
|
+
onProgress?: IProgressCallback,
|
|
196
228
|
): Promise<Result<ICoolifyDeployResult, Error>> {
|
|
197
229
|
if (!options.uuid && !options.tag) {
|
|
198
|
-
return err(new Error(
|
|
230
|
+
return err(new Error("Either uuid or tag is required"));
|
|
199
231
|
}
|
|
200
232
|
|
|
201
|
-
const appId = options.uuid?.slice(0, 8) || options.tag ||
|
|
202
|
-
onProgress?.(5, `Preparing deployment for ${appId}...`)
|
|
233
|
+
const appId = options.uuid?.slice(0, 8) || options.tag || "unknown";
|
|
234
|
+
onProgress?.(5, `Preparing deployment for ${appId}...`);
|
|
203
235
|
|
|
204
|
-
log.info(`Deploying application ${options.uuid || options.tag}`)
|
|
236
|
+
log.info(`Deploying application ${options.uuid || options.tag}`);
|
|
205
237
|
|
|
206
|
-
onProgress?.(25,
|
|
207
|
-
onProgress?.(50,
|
|
238
|
+
onProgress?.(25, "Validating deployment configuration");
|
|
239
|
+
onProgress?.(50, "Triggering build pipeline...");
|
|
208
240
|
|
|
209
241
|
// Build query parameters for deploy endpoint
|
|
210
|
-
const params = new URLSearchParams()
|
|
211
|
-
if (options.uuid) params.set(
|
|
212
|
-
if (options.tag) params.set(
|
|
213
|
-
if (options.force) params.set(
|
|
242
|
+
const params = new URLSearchParams();
|
|
243
|
+
if (options.uuid) params.set("uuid", options.uuid);
|
|
244
|
+
if (options.tag) params.set("tag", options.tag);
|
|
245
|
+
if (options.force) params.set("force", "true");
|
|
214
246
|
|
|
215
|
-
const endpoint = `/deploy${params.toString() ? `?${params.toString()}` :
|
|
247
|
+
const endpoint = `/deploy${params.toString() ? `?${params.toString()}` : ""}`;
|
|
216
248
|
|
|
217
249
|
const result = await this.request<{
|
|
218
250
|
deployments: Array<{
|
|
219
|
-
message: string
|
|
220
|
-
resource_uuid: string
|
|
221
|
-
deployment_uuid: string
|
|
222
|
-
}
|
|
251
|
+
message: string;
|
|
252
|
+
resource_uuid: string;
|
|
253
|
+
deployment_uuid: string;
|
|
254
|
+
}>;
|
|
223
255
|
}>(endpoint, {
|
|
224
|
-
method:
|
|
225
|
-
})
|
|
256
|
+
method: "GET",
|
|
257
|
+
});
|
|
226
258
|
|
|
227
259
|
if (result.error) {
|
|
228
|
-
log.error(`Deployment failed: ${result.error}`)
|
|
229
|
-
return err(new Error(result.error))
|
|
260
|
+
log.error(`Deployment failed: ${result.error}`);
|
|
261
|
+
return err(new Error(result.error));
|
|
230
262
|
}
|
|
231
263
|
|
|
232
264
|
// Response is { deployments: [{ message, resource_uuid, deployment_uuid }] }
|
|
233
|
-
const deployments = result.data?.deployments || []
|
|
265
|
+
const deployments = result.data?.deployments || [];
|
|
234
266
|
if (deployments.length === 0) {
|
|
235
|
-
log.error(
|
|
236
|
-
return err(
|
|
267
|
+
log.error("No deployments started");
|
|
268
|
+
return err(
|
|
269
|
+
new Error("No deployments started - check application configuration"),
|
|
270
|
+
);
|
|
237
271
|
}
|
|
238
272
|
|
|
239
|
-
const deployment = deployments[0]
|
|
273
|
+
const deployment = deployments[0];
|
|
240
274
|
|
|
241
|
-
onProgress?.(90,
|
|
242
|
-
onProgress?.(100,
|
|
275
|
+
onProgress?.(90, "Build started on Coolify server");
|
|
276
|
+
onProgress?.(100, "Deployment triggered");
|
|
243
277
|
|
|
244
|
-
log.success(`Deployment started: ${deployment.deployment_uuid}`)
|
|
278
|
+
log.success(`Deployment started: ${deployment.deployment_uuid}`);
|
|
245
279
|
return ok({
|
|
246
280
|
success: true,
|
|
247
281
|
deploymentUuid: deployment.deployment_uuid,
|
|
248
282
|
resourceUuid: deployment.resource_uuid,
|
|
249
|
-
})
|
|
283
|
+
});
|
|
250
284
|
}
|
|
251
285
|
|
|
252
286
|
/**
|
|
@@ -266,27 +300,29 @@ export class CoolifyService {
|
|
|
266
300
|
*/
|
|
267
301
|
async createApplication(
|
|
268
302
|
options: ICoolifyAppOptions,
|
|
269
|
-
onProgress?: IProgressCallback
|
|
303
|
+
onProgress?: IProgressCallback,
|
|
270
304
|
): Promise<Result<ICoolifyAppResult, Error>> {
|
|
271
|
-
onProgress?.(5, `Preparing app "${options.name}"`)
|
|
305
|
+
onProgress?.(5, `Preparing app "${options.name}"`);
|
|
272
306
|
|
|
273
|
-
const appType = options.type ||
|
|
274
|
-
log.info(`Creating application ${options.name} (type: ${appType})`)
|
|
307
|
+
const appType = options.type || "public";
|
|
308
|
+
log.info(`Creating application ${options.name} (type: ${appType})`);
|
|
275
309
|
|
|
276
|
-
onProgress?.(25, `Validating server ${options.serverUuid.slice(0, 8)}...`)
|
|
277
|
-
onProgress?.(50,
|
|
310
|
+
onProgress?.(25, `Validating server ${options.serverUuid.slice(0, 8)}...`);
|
|
311
|
+
onProgress?.(50, "Sending creation request to Coolify API...");
|
|
278
312
|
|
|
279
313
|
// Determine endpoint based on application type
|
|
280
314
|
const endpointMap: Record<string, string> = {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
315
|
+
public: "/applications/public",
|
|
316
|
+
"private-github-app": "/applications/private-github-app",
|
|
317
|
+
"private-deploy-key": "/applications/private-deploy-key",
|
|
318
|
+
dockerfile: "/applications/dockerfile",
|
|
319
|
+
"docker-image": "/applications/docker-image",
|
|
320
|
+
"docker-compose": "/applications/docker-compose",
|
|
321
|
+
dockerimage: "/applications/dockerimage",
|
|
322
|
+
dockercompose: "/applications/dockercompose",
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const endpoint = endpointMap[appType] || "/applications/public";
|
|
290
326
|
|
|
291
327
|
// Build request body based on application type
|
|
292
328
|
const body: Record<string, unknown> = {
|
|
@@ -295,60 +331,64 @@ export class CoolifyService {
|
|
|
295
331
|
project_uuid: options.projectUuid,
|
|
296
332
|
environment_uuid: options.environmentUuid,
|
|
297
333
|
server_uuid: options.serverUuid,
|
|
298
|
-
}
|
|
334
|
+
};
|
|
299
335
|
|
|
300
336
|
// Type-specific fields
|
|
301
|
-
if (
|
|
337
|
+
if (
|
|
338
|
+
appType === "public" ||
|
|
339
|
+
appType === "private-github-app" ||
|
|
340
|
+
appType === "private-deploy-key"
|
|
341
|
+
) {
|
|
302
342
|
if (options.githubRepoUrl) {
|
|
303
343
|
// Coolify expects 'user/repo' format, not full URL
|
|
304
344
|
body.git_repository = options.githubRepoUrl
|
|
305
|
-
.replace(/^https?:\/\/github\.com\//,
|
|
306
|
-
.replace(/\.git$/,
|
|
345
|
+
.replace(/^https?:\/\/github\.com\//, "")
|
|
346
|
+
.replace(/\.git$/, "");
|
|
307
347
|
}
|
|
308
348
|
if (options.githubAppUuid) {
|
|
309
|
-
body.github_app_uuid = options.githubAppUuid
|
|
349
|
+
body.github_app_uuid = options.githubAppUuid;
|
|
310
350
|
}
|
|
311
|
-
body.git_branch = options.branch ||
|
|
312
|
-
body.build_pack = options.buildPack ||
|
|
351
|
+
body.git_branch = options.branch || "main";
|
|
352
|
+
body.build_pack = options.buildPack || "dockerfile";
|
|
313
353
|
if (options.portsExposes) {
|
|
314
|
-
body.ports_exposes = options.portsExposes
|
|
354
|
+
body.ports_exposes = options.portsExposes;
|
|
315
355
|
}
|
|
316
356
|
// Dockerfile / Docker Compose configuration
|
|
317
357
|
if (options.dockerfileLocation) {
|
|
318
|
-
body.dockerfile_location = options.dockerfileLocation
|
|
358
|
+
body.dockerfile_location = options.dockerfileLocation;
|
|
319
359
|
}
|
|
320
360
|
if (options.dockerComposeLocation) {
|
|
321
|
-
body.docker_compose_location = options.dockerComposeLocation
|
|
361
|
+
body.docker_compose_location = options.dockerComposeLocation;
|
|
322
362
|
}
|
|
323
363
|
if (options.baseDirectory) {
|
|
324
|
-
body.base_directory = options.baseDirectory
|
|
364
|
+
body.base_directory = options.baseDirectory;
|
|
325
365
|
}
|
|
326
|
-
} else if (appType ===
|
|
327
|
-
body.docker_image = options.dockerImage
|
|
328
|
-
} else if (appType ===
|
|
329
|
-
body.docker_compose = options.dockerCompose
|
|
366
|
+
} else if (appType === "docker-image" && options.dockerImage) {
|
|
367
|
+
body.docker_image = options.dockerImage;
|
|
368
|
+
} else if (appType === "docker-compose" && options.dockerCompose) {
|
|
369
|
+
body.docker_compose = options.dockerCompose;
|
|
330
370
|
}
|
|
331
371
|
|
|
332
|
-
log.debug(`Create application body: ${JSON.stringify(body, null, 2)}`)
|
|
333
|
-
log.debug(`Endpoint: POST ${endpoint}`)
|
|
372
|
+
log.debug(`Create application body: ${JSON.stringify(body, null, 2)}`);
|
|
373
|
+
log.debug(`Endpoint: POST ${endpoint}`);
|
|
334
374
|
|
|
335
375
|
const result = await this.request<{ uuid: string }>(endpoint, {
|
|
336
|
-
method:
|
|
376
|
+
method: "POST",
|
|
337
377
|
body: JSON.stringify(body),
|
|
338
|
-
})
|
|
378
|
+
});
|
|
339
379
|
|
|
340
380
|
if (result.error) {
|
|
341
|
-
log.error(`Failed to create application: ${result.error}`)
|
|
342
|
-
return err(new Error(result.error))
|
|
381
|
+
log.error(`Failed to create application: ${result.error}`);
|
|
382
|
+
return err(new Error(result.error));
|
|
343
383
|
}
|
|
344
384
|
|
|
345
|
-
onProgress?.(100, `Application "${options.name}" created`)
|
|
385
|
+
onProgress?.(100, `Application "${options.name}" created`);
|
|
346
386
|
|
|
347
|
-
log.success(`Application created: ${result.data?.uuid}`)
|
|
387
|
+
log.success(`Application created: ${result.data?.uuid}`);
|
|
348
388
|
return ok({
|
|
349
389
|
success: true,
|
|
350
390
|
uuid: result.data?.uuid,
|
|
351
|
-
})
|
|
391
|
+
});
|
|
352
392
|
}
|
|
353
393
|
|
|
354
394
|
/**
|
|
@@ -360,44 +400,50 @@ export class CoolifyService {
|
|
|
360
400
|
*/
|
|
361
401
|
async setEnvironmentVariables(
|
|
362
402
|
appUuid: string,
|
|
363
|
-
envVars: Record<string, string
|
|
403
|
+
envVars: Record<string, string>,
|
|
364
404
|
): Promise<Result<void, Error>> {
|
|
365
|
-
log.info(
|
|
405
|
+
log.info(
|
|
406
|
+
`Setting ${Object.keys(envVars).length} environment variables for ${appUuid}`,
|
|
407
|
+
);
|
|
366
408
|
|
|
367
409
|
// Coolify API: POST to create, PATCH to update existing
|
|
368
410
|
for (const [key, value] of Object.entries(envVars)) {
|
|
369
411
|
const result = await this.request(`/applications/${appUuid}/envs`, {
|
|
370
|
-
method:
|
|
412
|
+
method: "POST",
|
|
371
413
|
body: JSON.stringify({ key, value, is_preview: false }),
|
|
372
|
-
})
|
|
414
|
+
});
|
|
373
415
|
|
|
374
|
-
if (result.error && result.error.includes(
|
|
416
|
+
if (result.error && result.error.includes("already exists")) {
|
|
375
417
|
// Var exists — DELETE + re-POST (Coolify PATCH on envs returns 404)
|
|
376
|
-
const listResult = await this.request<
|
|
377
|
-
|
|
418
|
+
const listResult = await this.request<
|
|
419
|
+
Array<{ uuid: string; key: string }>
|
|
420
|
+
>(`/applications/${appUuid}/envs`);
|
|
421
|
+
const existing = listResult.data?.find((v) => v.key === key);
|
|
378
422
|
if (existing) {
|
|
379
|
-
await this.request(`/applications/${appUuid}/envs/${existing.uuid}`, {
|
|
423
|
+
await this.request(`/applications/${appUuid}/envs/${existing.uuid}`, {
|
|
424
|
+
method: "DELETE",
|
|
425
|
+
});
|
|
380
426
|
const repost = await this.request(`/applications/${appUuid}/envs`, {
|
|
381
|
-
method:
|
|
427
|
+
method: "POST",
|
|
382
428
|
body: JSON.stringify({ key, value, is_preview: false }),
|
|
383
|
-
})
|
|
429
|
+
});
|
|
384
430
|
if (repost.error) {
|
|
385
|
-
log.error(`Failed to update env var ${key}: ${repost.error}`)
|
|
386
|
-
return err(new Error(`Failed to update ${key}: ${repost.error}`))
|
|
431
|
+
log.error(`Failed to update env var ${key}: ${repost.error}`);
|
|
432
|
+
return err(new Error(`Failed to update ${key}: ${repost.error}`));
|
|
387
433
|
}
|
|
388
|
-
log.debug(`Updated existing env var: ${key}`)
|
|
434
|
+
log.debug(`Updated existing env var: ${key}`);
|
|
389
435
|
} else {
|
|
390
|
-
log.error(`Failed to set env var ${key}: ${result.error}`)
|
|
391
|
-
return err(new Error(`Failed to set ${key}: ${result.error}`))
|
|
436
|
+
log.error(`Failed to set env var ${key}: ${result.error}`);
|
|
437
|
+
return err(new Error(`Failed to set ${key}: ${result.error}`));
|
|
392
438
|
}
|
|
393
439
|
} else if (result.error) {
|
|
394
|
-
log.error(`Failed to set env var ${key}: ${result.error}`)
|
|
395
|
-
return err(new Error(`Failed to set ${key}: ${result.error}`))
|
|
440
|
+
log.error(`Failed to set env var ${key}: ${result.error}`);
|
|
441
|
+
return err(new Error(`Failed to set ${key}: ${result.error}`));
|
|
396
442
|
}
|
|
397
443
|
}
|
|
398
444
|
|
|
399
|
-
log.success(`${Object.keys(envVars).length} environment variables set`)
|
|
400
|
-
return ok(undefined)
|
|
445
|
+
log.success(`${Object.keys(envVars).length} environment variables set`);
|
|
446
|
+
return ok(undefined);
|
|
401
447
|
}
|
|
402
448
|
|
|
403
449
|
/**
|
|
@@ -407,21 +453,21 @@ export class CoolifyService {
|
|
|
407
453
|
* @returns Result with environment variables or error
|
|
408
454
|
*/
|
|
409
455
|
async getEnvironmentVariables(
|
|
410
|
-
appUuid: string
|
|
456
|
+
appUuid: string,
|
|
411
457
|
): Promise<Result<ICoolifyEnvVar[], Error>> {
|
|
412
|
-
log.info(`Getting environment variables for ${appUuid}`)
|
|
458
|
+
log.info(`Getting environment variables for ${appUuid}`);
|
|
413
459
|
|
|
414
460
|
const result = await this.request<ICoolifyEnvVar[]>(
|
|
415
|
-
`/applications/${appUuid}/envs
|
|
416
|
-
)
|
|
461
|
+
`/applications/${appUuid}/envs`,
|
|
462
|
+
);
|
|
417
463
|
|
|
418
464
|
if (result.error) {
|
|
419
|
-
log.error(`Failed to get env vars: ${result.error}`)
|
|
420
|
-
return err(new Error(result.error))
|
|
465
|
+
log.error(`Failed to get env vars: ${result.error}`);
|
|
466
|
+
return err(new Error(result.error));
|
|
421
467
|
}
|
|
422
468
|
|
|
423
|
-
log.success(`Environment variables retrieved for ${appUuid}`)
|
|
424
|
-
return ok(result.data || [])
|
|
469
|
+
log.success(`Environment variables retrieved for ${appUuid}`);
|
|
470
|
+
return ok(result.data || []);
|
|
425
471
|
}
|
|
426
472
|
|
|
427
473
|
/**
|
|
@@ -438,46 +484,52 @@ export class CoolifyService {
|
|
|
438
484
|
appUuid: string,
|
|
439
485
|
key: string,
|
|
440
486
|
value: string,
|
|
441
|
-
|
|
487
|
+
_isBuildTime: boolean = false,
|
|
442
488
|
): Promise<Result<void, Error>> {
|
|
443
|
-
log.info(`Setting environment variable ${key} for ${appUuid}`)
|
|
489
|
+
log.info(`Setting environment variable ${key} for ${appUuid}`);
|
|
444
490
|
|
|
445
491
|
// Check if variable already exists
|
|
446
|
-
const existingVars = await this.getEnvironmentVariables(appUuid)
|
|
492
|
+
const existingVars = await this.getEnvironmentVariables(appUuid);
|
|
447
493
|
if (isErr(existingVars)) {
|
|
448
|
-
return err(existingVars.error)
|
|
494
|
+
return err(existingVars.error);
|
|
449
495
|
}
|
|
450
496
|
|
|
451
|
-
const exists = existingVars.value.some(ev => ev.key === key)
|
|
497
|
+
const exists = existingVars.value.some((ev) => ev.key === key);
|
|
452
498
|
|
|
453
499
|
if (exists) {
|
|
454
500
|
// Use PATCH to update existing variable
|
|
455
|
-
log.debug(`Variable ${key} exists, using PATCH to update`)
|
|
456
|
-
const result = await this.request<{ uuid: string }>(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
501
|
+
log.debug(`Variable ${key} exists, using PATCH to update`);
|
|
502
|
+
const result = await this.request<{ uuid: string }>(
|
|
503
|
+
`/applications/${appUuid}/envs`,
|
|
504
|
+
{
|
|
505
|
+
method: "PATCH",
|
|
506
|
+
body: JSON.stringify({ key, value }),
|
|
507
|
+
},
|
|
508
|
+
);
|
|
460
509
|
|
|
461
510
|
if (result.error) {
|
|
462
|
-
log.error(`Failed to update env var: ${result.error}`)
|
|
463
|
-
return err(new Error(result.error))
|
|
511
|
+
log.error(`Failed to update env var: ${result.error}`);
|
|
512
|
+
return err(new Error(result.error));
|
|
464
513
|
}
|
|
465
514
|
} else {
|
|
466
515
|
// Use POST to create new variable
|
|
467
|
-
log.debug(`Variable ${key} does not exist, using POST to create`)
|
|
468
|
-
const result = await this.request<{ uuid: string }>(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
516
|
+
log.debug(`Variable ${key} does not exist, using POST to create`);
|
|
517
|
+
const result = await this.request<{ uuid: string }>(
|
|
518
|
+
`/applications/${appUuid}/envs`,
|
|
519
|
+
{
|
|
520
|
+
method: "POST",
|
|
521
|
+
body: JSON.stringify({ key, value }),
|
|
522
|
+
},
|
|
523
|
+
);
|
|
472
524
|
|
|
473
525
|
if (result.error) {
|
|
474
|
-
log.error(`Failed to create env var: ${result.error}`)
|
|
475
|
-
return err(new Error(result.error))
|
|
526
|
+
log.error(`Failed to create env var: ${result.error}`);
|
|
527
|
+
return err(new Error(result.error));
|
|
476
528
|
}
|
|
477
529
|
}
|
|
478
530
|
|
|
479
|
-
log.success(`Environment variable ${key} set for ${appUuid}`)
|
|
480
|
-
return ok(undefined)
|
|
531
|
+
log.success(`Environment variable ${key} set for ${appUuid}`);
|
|
532
|
+
return ok(undefined);
|
|
481
533
|
}
|
|
482
534
|
|
|
483
535
|
/**
|
|
@@ -489,33 +541,36 @@ export class CoolifyService {
|
|
|
489
541
|
*/
|
|
490
542
|
async deleteEnvironmentVariable(
|
|
491
543
|
appUuid: string,
|
|
492
|
-
key: string
|
|
544
|
+
key: string,
|
|
493
545
|
): Promise<Result<void, Error>> {
|
|
494
|
-
log.info(`Deleting environment variable ${key} from ${appUuid}`)
|
|
546
|
+
log.info(`Deleting environment variable ${key} from ${appUuid}`);
|
|
495
547
|
|
|
496
548
|
// First get all env vars to find the UUID of the one to delete
|
|
497
|
-
const envVarsResult = await this.getEnvironmentVariables(appUuid)
|
|
549
|
+
const envVarsResult = await this.getEnvironmentVariables(appUuid);
|
|
498
550
|
if (isErr(envVarsResult)) {
|
|
499
|
-
return err(envVarsResult.error)
|
|
551
|
+
return err(envVarsResult.error);
|
|
500
552
|
}
|
|
501
553
|
|
|
502
|
-
const envVar = envVarsResult.value.find(ev => ev.key === key)
|
|
554
|
+
const envVar = envVarsResult.value.find((ev) => ev.key === key);
|
|
503
555
|
if (!envVar) {
|
|
504
|
-
log.error(`Environment variable ${key} not found`)
|
|
505
|
-
return err(new Error(`Environment variable ${key} not found`))
|
|
556
|
+
log.error(`Environment variable ${key} not found`);
|
|
557
|
+
return err(new Error(`Environment variable ${key} not found`));
|
|
506
558
|
}
|
|
507
559
|
|
|
508
|
-
const result = await this.request(
|
|
509
|
-
|
|
510
|
-
|
|
560
|
+
const result = await this.request(
|
|
561
|
+
`/applications/${appUuid}/envs/${envVar.uuid}`,
|
|
562
|
+
{
|
|
563
|
+
method: "DELETE",
|
|
564
|
+
},
|
|
565
|
+
);
|
|
511
566
|
|
|
512
567
|
if (result.error) {
|
|
513
|
-
log.error(`Failed to delete env var: ${result.error}`)
|
|
514
|
-
return err(new Error(result.error))
|
|
568
|
+
log.error(`Failed to delete env var: ${result.error}`);
|
|
569
|
+
return err(new Error(result.error));
|
|
515
570
|
}
|
|
516
571
|
|
|
517
|
-
log.success(`Environment variable ${key} deleted from ${appUuid}`)
|
|
518
|
-
return ok(undefined)
|
|
572
|
+
log.success(`Environment variable ${key} deleted from ${appUuid}`);
|
|
573
|
+
return ok(undefined);
|
|
519
574
|
}
|
|
520
575
|
|
|
521
576
|
/**
|
|
@@ -524,33 +579,44 @@ export class CoolifyService {
|
|
|
524
579
|
* @param appUuid - Application UUID
|
|
525
580
|
* @returns Result with status or error
|
|
526
581
|
*/
|
|
527
|
-
async getApplicationStatus(
|
|
528
|
-
appUuid: string
|
|
529
|
-
): Promise<Result<string, Error>> {
|
|
582
|
+
async getApplicationStatus(appUuid: string): Promise<Result<string, Error>> {
|
|
530
583
|
const result = await this.request<{ status: string }>(
|
|
531
|
-
`/applications/${appUuid}
|
|
532
|
-
)
|
|
584
|
+
`/applications/${appUuid}`,
|
|
585
|
+
);
|
|
533
586
|
|
|
534
587
|
if (result.error) {
|
|
535
|
-
return err(new Error(result.error))
|
|
588
|
+
return err(new Error(result.error));
|
|
536
589
|
}
|
|
537
590
|
|
|
538
|
-
return ok(result.data?.status ||
|
|
591
|
+
return ok(result.data?.status || "unknown");
|
|
539
592
|
}
|
|
540
593
|
|
|
541
594
|
/**
|
|
542
595
|
* Lists available servers in Coolify.
|
|
543
596
|
*
|
|
597
|
+
* @param page - Optional page number for pagination
|
|
598
|
+
* @param perPage - Optional number of items per page
|
|
544
599
|
* @returns Result with servers or error
|
|
545
600
|
*/
|
|
546
|
-
async listServers(
|
|
547
|
-
|
|
601
|
+
async listServers(
|
|
602
|
+
page?: number,
|
|
603
|
+
perPage?: number,
|
|
604
|
+
): Promise<Result<ICoolifyServer[], Error>> {
|
|
605
|
+
let endpoint = "/servers";
|
|
606
|
+
const params = new URLSearchParams();
|
|
607
|
+
if (page) params.set("page", page.toString());
|
|
608
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
609
|
+
if (params.toString()) {
|
|
610
|
+
endpoint += `?${params.toString()}`;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const result = await this.request<ICoolifyServer[]>(endpoint);
|
|
548
614
|
|
|
549
615
|
if (result.error) {
|
|
550
|
-
return err(new Error(result.error))
|
|
616
|
+
return err(new Error(result.error));
|
|
551
617
|
}
|
|
552
618
|
|
|
553
|
-
return ok(result.data || [])
|
|
619
|
+
return ok(result.data || []);
|
|
554
620
|
}
|
|
555
621
|
|
|
556
622
|
/**
|
|
@@ -559,46 +625,78 @@ export class CoolifyService {
|
|
|
559
625
|
* @param serverUuid - Server UUID
|
|
560
626
|
* @returns Result with server details or error
|
|
561
627
|
*/
|
|
562
|
-
async getServer(
|
|
563
|
-
serverUuid
|
|
564
|
-
): Promise<Result<ICoolifyServer, Error>> {
|
|
565
|
-
log.info(`Getting server details for ${serverUuid}`)
|
|
628
|
+
async getServer(serverUuid: string): Promise<Result<ICoolifyServer, Error>> {
|
|
629
|
+
log.info(`Getting server details for ${serverUuid}`);
|
|
566
630
|
|
|
567
|
-
const result = await this.request<ICoolifyServer>(`/servers/${serverUuid}`)
|
|
631
|
+
const result = await this.request<ICoolifyServer>(`/servers/${serverUuid}`);
|
|
568
632
|
|
|
569
633
|
if (result.error) {
|
|
570
|
-
log.error(`Failed to get server: ${result.error}`)
|
|
571
|
-
return err(new Error(result.error))
|
|
634
|
+
log.error(`Failed to get server: ${result.error}`);
|
|
635
|
+
return err(new Error(result.error));
|
|
572
636
|
}
|
|
573
637
|
|
|
574
|
-
log.success(`Server details retrieved: ${serverUuid}`)
|
|
575
|
-
return ok(result.data as ICoolifyServer)
|
|
638
|
+
log.success(`Server details retrieved: ${serverUuid}`);
|
|
639
|
+
return ok(result.data as ICoolifyServer);
|
|
576
640
|
}
|
|
577
641
|
|
|
578
642
|
/**
|
|
579
643
|
* Lists all GitHub Apps configured in Coolify.
|
|
580
644
|
*
|
|
645
|
+
* @param page - Optional page number for pagination
|
|
646
|
+
* @param perPage - Optional number of items per page
|
|
581
647
|
* @returns Result with GitHub Apps list or error
|
|
582
648
|
*/
|
|
583
|
-
async listGithubApps(
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
649
|
+
async listGithubApps(
|
|
650
|
+
page?: number,
|
|
651
|
+
perPage?: number,
|
|
652
|
+
): Promise<
|
|
653
|
+
Result<
|
|
654
|
+
Array<{ id: number; uuid: string; name: string; is_public: boolean }>,
|
|
655
|
+
Error
|
|
656
|
+
>
|
|
657
|
+
> {
|
|
658
|
+
let endpoint = "/github-apps";
|
|
659
|
+
const params = new URLSearchParams();
|
|
660
|
+
if (page) params.set("page", page.toString());
|
|
661
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
662
|
+
if (params.toString()) {
|
|
663
|
+
endpoint += `?${params.toString()}`;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const result =
|
|
667
|
+
await this.request<
|
|
668
|
+
Array<{ id: number; uuid: string; name: string; is_public: boolean }>
|
|
669
|
+
>(endpoint);
|
|
670
|
+
if (result.error) return err(new Error(result.error));
|
|
671
|
+
return ok(result.data || []);
|
|
587
672
|
}
|
|
588
673
|
|
|
589
674
|
/**
|
|
590
675
|
* Lists all projects.
|
|
591
676
|
*
|
|
677
|
+
* @param page - Optional page number for pagination
|
|
678
|
+
* @param perPage - Optional number of items per page
|
|
592
679
|
* @returns Result with projects list or error
|
|
593
680
|
*/
|
|
594
|
-
async listProjects(
|
|
595
|
-
|
|
681
|
+
async listProjects(
|
|
682
|
+
page?: number,
|
|
683
|
+
perPage?: number,
|
|
684
|
+
): Promise<Result<ICoolifyProject[], Error>> {
|
|
685
|
+
let endpoint = "/projects";
|
|
686
|
+
const params = new URLSearchParams();
|
|
687
|
+
if (page) params.set("page", page.toString());
|
|
688
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
689
|
+
if (params.toString()) {
|
|
690
|
+
endpoint += `?${params.toString()}`;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const result = await this.request<ICoolifyProject[]>(endpoint);
|
|
596
694
|
|
|
597
695
|
if (result.error) {
|
|
598
|
-
return err(new Error(result.error))
|
|
696
|
+
return err(new Error(result.error));
|
|
599
697
|
}
|
|
600
698
|
|
|
601
|
-
return ok(result.data || [])
|
|
699
|
+
return ok(result.data || []);
|
|
602
700
|
}
|
|
603
701
|
|
|
604
702
|
/**
|
|
@@ -610,22 +708,22 @@ export class CoolifyService {
|
|
|
610
708
|
*/
|
|
611
709
|
async createProject(
|
|
612
710
|
name: string,
|
|
613
|
-
description?: string
|
|
711
|
+
description?: string,
|
|
614
712
|
): Promise<Result<ICoolifyProject, Error>> {
|
|
615
|
-
log.info(`Creating project: ${name}`)
|
|
713
|
+
log.info(`Creating project: ${name}`);
|
|
616
714
|
|
|
617
|
-
const result = await this.request<ICoolifyProject>(
|
|
618
|
-
method:
|
|
619
|
-
body: JSON.stringify({ name, description: description ||
|
|
620
|
-
})
|
|
715
|
+
const result = await this.request<ICoolifyProject>("/projects", {
|
|
716
|
+
method: "POST",
|
|
717
|
+
body: JSON.stringify({ name, description: description || "" }),
|
|
718
|
+
});
|
|
621
719
|
|
|
622
720
|
if (result.error) {
|
|
623
|
-
log.error(`Failed to create project: ${result.error}`)
|
|
624
|
-
return err(new Error(result.error))
|
|
721
|
+
log.error(`Failed to create project: ${result.error}`);
|
|
722
|
+
return err(new Error(result.error));
|
|
625
723
|
}
|
|
626
724
|
|
|
627
|
-
log.success(`Project created: ${result.data?.uuid}`)
|
|
628
|
-
return ok(result.data!)
|
|
725
|
+
log.success(`Project created: ${result.data?.uuid}`);
|
|
726
|
+
return ok(result.data!);
|
|
629
727
|
}
|
|
630
728
|
|
|
631
729
|
/**
|
|
@@ -635,36 +733,49 @@ export class CoolifyService {
|
|
|
635
733
|
* @returns Result with environments list or error
|
|
636
734
|
*/
|
|
637
735
|
async getProjectEnvironments(
|
|
638
|
-
projectUuid: string
|
|
736
|
+
projectUuid: string,
|
|
639
737
|
): Promise<Result<ICoolifyEnvironment[], Error>> {
|
|
640
|
-
log.info(`Getting environments for project ${projectUuid}`)
|
|
738
|
+
log.info(`Getting environments for project ${projectUuid}`);
|
|
641
739
|
|
|
642
740
|
const result = await this.request<{ environments: ICoolifyEnvironment[] }>(
|
|
643
|
-
`/projects/${projectUuid}
|
|
644
|
-
)
|
|
741
|
+
`/projects/${projectUuid}`,
|
|
742
|
+
);
|
|
645
743
|
|
|
646
744
|
if (result.error) {
|
|
647
|
-
log.error(`Failed to get environments: ${result.error}`)
|
|
648
|
-
return err(new Error(result.error))
|
|
745
|
+
log.error(`Failed to get environments: ${result.error}`);
|
|
746
|
+
return err(new Error(result.error));
|
|
649
747
|
}
|
|
650
748
|
|
|
651
|
-
log.success(`Environments retrieved for project ${projectUuid}`)
|
|
652
|
-
return ok(result.data?.environments || [])
|
|
749
|
+
log.success(`Environments retrieved for project ${projectUuid}`);
|
|
750
|
+
return ok(result.data?.environments || []);
|
|
653
751
|
}
|
|
654
752
|
|
|
655
753
|
/**
|
|
656
754
|
* Lists all teams.
|
|
657
755
|
*
|
|
756
|
+
* @param page - Optional page number for pagination
|
|
757
|
+
* @param perPage - Optional number of items per page
|
|
658
758
|
* @returns Result with teams list or error
|
|
659
759
|
*/
|
|
660
|
-
async listTeams(
|
|
661
|
-
|
|
760
|
+
async listTeams(
|
|
761
|
+
page?: number,
|
|
762
|
+
perPage?: number,
|
|
763
|
+
): Promise<Result<ICoolifyTeam[], Error>> {
|
|
764
|
+
let endpoint = "/teams";
|
|
765
|
+
const params = new URLSearchParams();
|
|
766
|
+
if (page) params.set("page", page.toString());
|
|
767
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
768
|
+
if (params.toString()) {
|
|
769
|
+
endpoint += `?${params.toString()}`;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const result = await this.request<ICoolifyTeam[]>(endpoint);
|
|
662
773
|
|
|
663
774
|
if (result.error) {
|
|
664
|
-
return err(new Error(result.error))
|
|
775
|
+
return err(new Error(result.error));
|
|
665
776
|
}
|
|
666
777
|
|
|
667
|
-
return ok(result.data || [])
|
|
778
|
+
return ok(result.data || []);
|
|
668
779
|
}
|
|
669
780
|
|
|
670
781
|
/**
|
|
@@ -674,17 +785,17 @@ export class CoolifyService {
|
|
|
674
785
|
* @returns Result with destinations or error
|
|
675
786
|
*/
|
|
676
787
|
async getServerDestinations(
|
|
677
|
-
serverUuid: string
|
|
788
|
+
serverUuid: string,
|
|
678
789
|
): Promise<Result<ICoolifyDestination[], Error>> {
|
|
679
790
|
const result = await this.request<{
|
|
680
|
-
destinations: ICoolifyDestination[]
|
|
681
|
-
}>(`/servers/${serverUuid}`)
|
|
791
|
+
destinations: ICoolifyDestination[];
|
|
792
|
+
}>(`/servers/${serverUuid}`);
|
|
682
793
|
|
|
683
794
|
if (result.error) {
|
|
684
|
-
return err(new Error(result.error))
|
|
795
|
+
return err(new Error(result.error));
|
|
685
796
|
}
|
|
686
797
|
|
|
687
|
-
return ok(result.data?.destinations || [])
|
|
798
|
+
return ok(result.data?.destinations || []);
|
|
688
799
|
}
|
|
689
800
|
|
|
690
801
|
/**
|
|
@@ -692,55 +803,79 @@ export class CoolifyService {
|
|
|
692
803
|
*
|
|
693
804
|
* @param teamId - Optional team ID to filter by
|
|
694
805
|
* @param projectId - Optional project ID to filter by
|
|
806
|
+
* @param page - Optional page number for pagination
|
|
807
|
+
* @param perPage - Optional number of items per page
|
|
695
808
|
* @returns Result with applications list or error
|
|
696
809
|
*/
|
|
697
810
|
async listApplications(
|
|
698
811
|
teamId?: string,
|
|
699
|
-
projectId?: string
|
|
812
|
+
projectId?: string,
|
|
813
|
+
page?: number,
|
|
814
|
+
perPage?: number,
|
|
700
815
|
): Promise<Result<ICoolifyApplication[], Error>> {
|
|
701
|
-
log.info(
|
|
702
|
-
|
|
703
|
-
let endpoint =
|
|
704
|
-
const params = new URLSearchParams()
|
|
705
|
-
if (teamId) params.set(
|
|
706
|
-
if (projectId) params.set(
|
|
816
|
+
log.info("Listing applications");
|
|
817
|
+
|
|
818
|
+
let endpoint = "/applications";
|
|
819
|
+
const params = new URLSearchParams();
|
|
820
|
+
if (teamId) params.set("team_id", teamId);
|
|
821
|
+
if (projectId) params.set("project_id", projectId);
|
|
822
|
+
if (page) params.set("page", page.toString());
|
|
823
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
707
824
|
if (params.toString()) {
|
|
708
|
-
endpoint += `?${params.toString()}
|
|
825
|
+
endpoint += `?${params.toString()}`;
|
|
709
826
|
}
|
|
710
827
|
|
|
711
|
-
const result = await this.request<ICoolifyApplication[]>(endpoint)
|
|
828
|
+
const result = await this.request<ICoolifyApplication[]>(endpoint);
|
|
712
829
|
|
|
713
830
|
if (result.error) {
|
|
714
|
-
log.error(`Failed to list applications: ${result.error}`)
|
|
715
|
-
return err(new Error(result.error))
|
|
831
|
+
log.error(`Failed to list applications: ${result.error}`);
|
|
832
|
+
return err(new Error(result.error));
|
|
716
833
|
}
|
|
717
834
|
|
|
718
|
-
log.success(`Listed ${result.data?.length || 0} applications`)
|
|
719
|
-
return ok(result.data || [])
|
|
835
|
+
log.success(`Listed ${result.data?.length || 0} applications`);
|
|
836
|
+
return ok(result.data || []);
|
|
720
837
|
}
|
|
721
838
|
|
|
722
839
|
/**
|
|
723
840
|
* Deletes an application.
|
|
724
841
|
*
|
|
725
842
|
* @param appUuid - Application UUID
|
|
843
|
+
* @param options - Delete options for cascade deletion
|
|
726
844
|
* @returns Result indicating success or error
|
|
727
845
|
*/
|
|
728
846
|
async deleteApplication(
|
|
729
|
-
appUuid: string
|
|
847
|
+
appUuid: string,
|
|
848
|
+
options?: {
|
|
849
|
+
deleteConfigurations?: boolean;
|
|
850
|
+
deleteVolumes?: boolean;
|
|
851
|
+
dockerCleanup?: boolean;
|
|
852
|
+
deleteConnectedNetworks?: boolean;
|
|
853
|
+
},
|
|
730
854
|
): Promise<Result<ICoolifyDeleteResult, Error>> {
|
|
731
|
-
log.info(`Deleting application ${appUuid}`)
|
|
855
|
+
log.info(`Deleting application ${appUuid}`);
|
|
732
856
|
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
857
|
+
const params = new URLSearchParams();
|
|
858
|
+
if (options?.deleteConfigurations)
|
|
859
|
+
params.set("delete_configurations", "true");
|
|
860
|
+
if (options?.deleteVolumes) params.set("delete_volumes", "true");
|
|
861
|
+
if (options?.dockerCleanup) params.set("docker_cleanup", "true");
|
|
862
|
+
if (options?.deleteConnectedNetworks)
|
|
863
|
+
params.set("delete_connected_networks", "true");
|
|
864
|
+
|
|
865
|
+
const queryString = params.toString();
|
|
866
|
+
const endpoint = `/applications/${appUuid}${queryString ? `?${queryString}` : ""}`;
|
|
867
|
+
|
|
868
|
+
const result = await this.request<ICoolifyDeleteResult>(endpoint, {
|
|
869
|
+
method: "DELETE",
|
|
870
|
+
});
|
|
736
871
|
|
|
737
872
|
if (result.error) {
|
|
738
|
-
log.error(`Failed to delete application: ${result.error}`)
|
|
739
|
-
return err(new Error(result.error))
|
|
873
|
+
log.error(`Failed to delete application: ${result.error}`);
|
|
874
|
+
return err(new Error(result.error));
|
|
740
875
|
}
|
|
741
876
|
|
|
742
|
-
log.success(`Application deleted: ${appUuid}`)
|
|
743
|
-
return ok({ success: true, message:
|
|
877
|
+
log.success(`Application deleted: ${appUuid}`);
|
|
878
|
+
return ok({ success: true, message: "Application deleted" });
|
|
744
879
|
}
|
|
745
880
|
|
|
746
881
|
/**
|
|
@@ -752,38 +887,45 @@ export class CoolifyService {
|
|
|
752
887
|
*/
|
|
753
888
|
async updateApplication(
|
|
754
889
|
appUuid: string,
|
|
755
|
-
options: ICoolifyUpdateOptions
|
|
890
|
+
options: ICoolifyUpdateOptions,
|
|
756
891
|
): Promise<Result<ICoolifyApplication, Error>> {
|
|
757
|
-
log.info(`Updating application ${appUuid}`)
|
|
758
|
-
|
|
759
|
-
const body: Record<string, unknown> = {}
|
|
760
|
-
if (options.name) body.name = options.name
|
|
761
|
-
if (options.description) body.description = options.description
|
|
762
|
-
if (options.buildPack) body.build_pack = options.buildPack
|
|
763
|
-
if (options.gitBranch) body.git_branch = options.gitBranch
|
|
764
|
-
if (options.portsExposes) body.ports_exposes = options.portsExposes
|
|
765
|
-
if (options.installCommand) body.install_command = options.installCommand
|
|
766
|
-
if (options.buildCommand) body.build_command = options.buildCommand
|
|
767
|
-
if (options.startCommand) body.start_command = options.startCommand
|
|
768
|
-
if (options.dockerfileLocation)
|
|
769
|
-
|
|
770
|
-
if (options.
|
|
771
|
-
if (options.
|
|
772
|
-
if (options.
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
body
|
|
778
|
-
|
|
892
|
+
log.info(`Updating application ${appUuid}`);
|
|
893
|
+
|
|
894
|
+
const body: Record<string, unknown> = {};
|
|
895
|
+
if (options.name) body.name = options.name;
|
|
896
|
+
if (options.description) body.description = options.description;
|
|
897
|
+
if (options.buildPack) body.build_pack = options.buildPack;
|
|
898
|
+
if (options.gitBranch) body.git_branch = options.gitBranch;
|
|
899
|
+
if (options.portsExposes) body.ports_exposes = options.portsExposes;
|
|
900
|
+
if (options.installCommand) body.install_command = options.installCommand;
|
|
901
|
+
if (options.buildCommand) body.build_command = options.buildCommand;
|
|
902
|
+
if (options.startCommand) body.start_command = options.startCommand;
|
|
903
|
+
if (options.dockerfileLocation)
|
|
904
|
+
body.dockerfile_location = options.dockerfileLocation;
|
|
905
|
+
if (options.baseDirectory) body.base_directory = options.baseDirectory;
|
|
906
|
+
if (options.domains) body.domains = options.domains;
|
|
907
|
+
if (options.dockerComposeDomains)
|
|
908
|
+
body.docker_compose_domains = options.dockerComposeDomains;
|
|
909
|
+
if (options.isForceHttpsEnabled !== undefined)
|
|
910
|
+
body.is_force_https_enabled = options.isForceHttpsEnabled;
|
|
911
|
+
if (options.isAutoDeployEnabled !== undefined)
|
|
912
|
+
body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
|
|
913
|
+
|
|
914
|
+
const result = await this.request<ICoolifyApplication>(
|
|
915
|
+
`/applications/${appUuid}`,
|
|
916
|
+
{
|
|
917
|
+
method: "PATCH",
|
|
918
|
+
body: JSON.stringify(body),
|
|
919
|
+
},
|
|
920
|
+
);
|
|
779
921
|
|
|
780
922
|
if (result.error) {
|
|
781
|
-
log.error(`Failed to update application: ${result.error}`)
|
|
782
|
-
return err(new Error(result.error))
|
|
923
|
+
log.error(`Failed to update application: ${result.error}`);
|
|
924
|
+
return err(new Error(result.error));
|
|
783
925
|
}
|
|
784
926
|
|
|
785
|
-
log.success(`Application updated: ${appUuid}`)
|
|
786
|
-
return ok(result.data as ICoolifyApplication)
|
|
927
|
+
log.success(`Application updated: ${appUuid}`);
|
|
928
|
+
return ok(result.data as ICoolifyApplication);
|
|
787
929
|
}
|
|
788
930
|
|
|
789
931
|
/**
|
|
@@ -795,37 +937,102 @@ export class CoolifyService {
|
|
|
795
937
|
*/
|
|
796
938
|
async getApplicationLogs(
|
|
797
939
|
appUuid: string,
|
|
798
|
-
options: ICoolifyLogsOptions = {}
|
|
940
|
+
options: ICoolifyLogsOptions = {},
|
|
799
941
|
): Promise<Result<ICoolifyLogs, Error>> {
|
|
800
|
-
log.info(`Getting logs for application ${appUuid}`)
|
|
942
|
+
log.info(`Getting logs for application ${appUuid}`);
|
|
801
943
|
|
|
802
|
-
const params = new URLSearchParams()
|
|
803
|
-
if (options.follow) params.set(
|
|
804
|
-
if (options.tail) params.set(
|
|
944
|
+
const params = new URLSearchParams();
|
|
945
|
+
if (options.follow) params.set("follow", "true");
|
|
946
|
+
if (options.tail) params.set("lines", options.tail.toString());
|
|
947
|
+
if (options.serviceName) params.set("service_name", options.serviceName);
|
|
805
948
|
|
|
806
|
-
const endpoint = `/applications/${appUuid}/logs${params.toString() ? `?${params.toString()}` :
|
|
949
|
+
const endpoint = `/applications/${appUuid}/logs${params.toString() ? `?${params.toString()}` : ""}`;
|
|
807
950
|
|
|
808
|
-
const result = await this.request<{ logs: string | string[] }>(endpoint)
|
|
951
|
+
const result = await this.request<{ logs: string | string[] }>(endpoint);
|
|
809
952
|
|
|
810
953
|
if (result.error) {
|
|
811
|
-
log.error(`Failed to get logs: ${result.error}`)
|
|
812
|
-
return err(new Error(result.error))
|
|
954
|
+
log.error(`Failed to get logs: ${result.error}`);
|
|
955
|
+
return err(new Error(result.error));
|
|
813
956
|
}
|
|
814
957
|
|
|
815
958
|
// Coolify API returns logs as a single newline-delimited string for
|
|
816
959
|
// docker-compose apps, but as string[] for single-container apps.
|
|
817
|
-
const rawLogs = result.data?.logs
|
|
960
|
+
const rawLogs = result.data?.logs;
|
|
818
961
|
const logsArray: string[] = Array.isArray(rawLogs)
|
|
819
962
|
? rawLogs
|
|
820
|
-
: typeof rawLogs ===
|
|
821
|
-
? rawLogs.split(
|
|
822
|
-
: []
|
|
963
|
+
: typeof rawLogs === "string"
|
|
964
|
+
? rawLogs.split("\n").filter((l: string) => l.length > 0)
|
|
965
|
+
: [];
|
|
823
966
|
|
|
824
|
-
log.success(`Logs retrieved for application: ${appUuid}`)
|
|
967
|
+
log.success(`Logs retrieved for application: ${appUuid}`);
|
|
825
968
|
return ok({
|
|
826
969
|
logs: logsArray,
|
|
827
970
|
timestamp: new Date().toISOString(),
|
|
828
|
-
})
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Executes a command on an application's running container.
|
|
976
|
+
*
|
|
977
|
+
* @param appUuid - Application UUID
|
|
978
|
+
* @param command - Shell command to execute
|
|
979
|
+
* @returns Result with command output or error
|
|
980
|
+
*/
|
|
981
|
+
async executeCommand(
|
|
982
|
+
appUuid: string,
|
|
983
|
+
command: string,
|
|
984
|
+
): Promise<Result<{ message?: string; response?: string }, Error>> {
|
|
985
|
+
log.info(`Executing command on application ${appUuid}`);
|
|
986
|
+
|
|
987
|
+
const result = await this.request<{
|
|
988
|
+
message?: string;
|
|
989
|
+
response?: string;
|
|
990
|
+
}>(`/applications/${appUuid}/execute-command`, {
|
|
991
|
+
method: "POST",
|
|
992
|
+
body: JSON.stringify({ command }),
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
if (result.error) {
|
|
996
|
+
log.error(`Failed to execute command: ${result.error}`);
|
|
997
|
+
return err(new Error(result.error));
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
log.success(`Command executed on ${appUuid}`);
|
|
1001
|
+
return ok(result.data || { message: "Command executed" });
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Bulk updates environment variables for an application.
|
|
1006
|
+
*
|
|
1007
|
+
* @param appUuid - Application UUID
|
|
1008
|
+
* @param envVars - Array of { key, value, is_preview? } objects
|
|
1009
|
+
* @returns Result indicating success or error
|
|
1010
|
+
*/
|
|
1011
|
+
async bulkUpdateEnvironmentVariables(
|
|
1012
|
+
appUuid: string,
|
|
1013
|
+
envVars: Array<{
|
|
1014
|
+
key: string;
|
|
1015
|
+
value: string;
|
|
1016
|
+
is_preview?: boolean;
|
|
1017
|
+
}>,
|
|
1018
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1019
|
+
log.info(`Bulk updating ${envVars.length} env vars for ${appUuid}`);
|
|
1020
|
+
|
|
1021
|
+
const result = await this.request<{ message: string }>(
|
|
1022
|
+
`/applications/${appUuid}/envs/bulk`,
|
|
1023
|
+
{
|
|
1024
|
+
method: "PATCH",
|
|
1025
|
+
body: JSON.stringify(envVars),
|
|
1026
|
+
},
|
|
1027
|
+
);
|
|
1028
|
+
|
|
1029
|
+
if (result.error) {
|
|
1030
|
+
log.error(`Failed to bulk update env vars: ${result.error}`);
|
|
1031
|
+
return err(new Error(result.error));
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
log.success(`Bulk updated ${envVars.length} env vars for ${appUuid}`);
|
|
1035
|
+
return ok(result.data || { message: "Environment variables updated" });
|
|
829
1036
|
}
|
|
830
1037
|
|
|
831
1038
|
/**
|
|
@@ -835,111 +1042,1131 @@ export class CoolifyService {
|
|
|
835
1042
|
* @returns Result with deployment history or error
|
|
836
1043
|
*/
|
|
837
1044
|
async getApplicationDeploymentHistory(
|
|
838
|
-
appUuid: string
|
|
1045
|
+
appUuid: string,
|
|
839
1046
|
): Promise<Result<ICoolifyDeployment[], Error>> {
|
|
840
|
-
log.info(`Getting deployment history for ${appUuid}`)
|
|
1047
|
+
log.info(`Getting deployment history for ${appUuid}`);
|
|
841
1048
|
|
|
842
|
-
//
|
|
843
|
-
|
|
1049
|
+
// Coolify API: /deployments/applications/{appUuid}
|
|
1050
|
+
// Response: { count: number, deployments: ICoolifyDeployment[] }
|
|
1051
|
+
const result = await this.request<{
|
|
1052
|
+
count: number;
|
|
1053
|
+
deployments: ICoolifyDeployment[];
|
|
1054
|
+
}>(`/deployments/applications/${appUuid}`);
|
|
844
1055
|
|
|
845
1056
|
if (result.error) {
|
|
846
|
-
log.error(`Failed to get deployment history: ${result.error}`)
|
|
847
|
-
return err(new Error(result.error))
|
|
1057
|
+
log.error(`Failed to get deployment history: ${result.error}`);
|
|
1058
|
+
return err(new Error(result.error));
|
|
848
1059
|
}
|
|
849
1060
|
|
|
850
|
-
log.success(`Deployment history retrieved for ${appUuid}`)
|
|
851
|
-
return ok(result.data?.deployments || [])
|
|
1061
|
+
log.success(`Deployment history retrieved for ${appUuid}`);
|
|
1062
|
+
return ok(result.data?.deployments || []);
|
|
852
1063
|
}
|
|
853
1064
|
|
|
854
1065
|
/**
|
|
855
1066
|
* Starts a stopped application.
|
|
1067
|
+
* Note: Coolify API uses GET for application start/stop/restart.
|
|
856
1068
|
*
|
|
857
1069
|
* @param appUuid - Application UUID
|
|
1070
|
+
* @param options - Optional start options (force, instant_deploy)
|
|
858
1071
|
* @returns Result with application status or error
|
|
859
1072
|
*/
|
|
860
1073
|
async startApplication(
|
|
861
|
-
appUuid: string
|
|
1074
|
+
appUuid: string,
|
|
1075
|
+
options?: { force?: boolean; instantDeploy?: boolean },
|
|
862
1076
|
): Promise<Result<ICoolifyApplication, Error>> {
|
|
863
|
-
log.info(`Starting application ${appUuid}`)
|
|
1077
|
+
log.info(`Starting application ${appUuid}`);
|
|
1078
|
+
|
|
1079
|
+
const params = new URLSearchParams();
|
|
1080
|
+
if (options?.force) params.set("force", "true");
|
|
1081
|
+
if (options?.instantDeploy) params.set("instant_deploy", "true");
|
|
1082
|
+
const queryString = params.toString();
|
|
1083
|
+
const endpoint = `/applications/${appUuid}/start${queryString ? `?${queryString}` : ""}`;
|
|
864
1084
|
|
|
865
|
-
const result = await this.request<ICoolifyApplication>(
|
|
866
|
-
method:
|
|
867
|
-
})
|
|
1085
|
+
const result = await this.request<ICoolifyApplication>(endpoint, {
|
|
1086
|
+
method: "GET",
|
|
1087
|
+
});
|
|
868
1088
|
|
|
869
1089
|
if (result.error) {
|
|
870
|
-
log.error(`Failed to start application: ${result.error}`)
|
|
871
|
-
return err(new Error(result.error))
|
|
1090
|
+
log.error(`Failed to start application: ${result.error}`);
|
|
1091
|
+
return err(new Error(result.error));
|
|
872
1092
|
}
|
|
873
1093
|
|
|
874
|
-
log.success(`Application started: ${appUuid}`)
|
|
875
|
-
return ok(result.data as ICoolifyApplication)
|
|
1094
|
+
log.success(`Application started: ${appUuid}`);
|
|
1095
|
+
return ok(result.data as ICoolifyApplication);
|
|
876
1096
|
}
|
|
877
1097
|
|
|
878
1098
|
/**
|
|
879
1099
|
* Stops a running application.
|
|
1100
|
+
* Note: Coolify API uses GET for application start/stop/restart.
|
|
880
1101
|
*
|
|
881
1102
|
* @param appUuid - Application UUID
|
|
882
1103
|
* @returns Result with application status or error
|
|
883
1104
|
*/
|
|
884
1105
|
async stopApplication(
|
|
885
|
-
appUuid: string
|
|
1106
|
+
appUuid: string,
|
|
886
1107
|
): Promise<Result<ICoolifyApplication, Error>> {
|
|
887
|
-
log.info(`Stopping application ${appUuid}`)
|
|
1108
|
+
log.info(`Stopping application ${appUuid}`);
|
|
888
1109
|
|
|
889
|
-
const result = await this.request<ICoolifyApplication>(
|
|
890
|
-
|
|
891
|
-
|
|
1110
|
+
const result = await this.request<ICoolifyApplication>(
|
|
1111
|
+
`/applications/${appUuid}/stop`,
|
|
1112
|
+
{ method: "GET" },
|
|
1113
|
+
);
|
|
892
1114
|
|
|
893
1115
|
if (result.error) {
|
|
894
|
-
log.error(`Failed to stop application: ${result.error}`)
|
|
895
|
-
return err(new Error(result.error))
|
|
1116
|
+
log.error(`Failed to stop application: ${result.error}`);
|
|
1117
|
+
return err(new Error(result.error));
|
|
896
1118
|
}
|
|
897
1119
|
|
|
898
|
-
log.success(`Application stopped: ${appUuid}`)
|
|
899
|
-
return ok(result.data as ICoolifyApplication)
|
|
1120
|
+
log.success(`Application stopped: ${appUuid}`);
|
|
1121
|
+
return ok(result.data as ICoolifyApplication);
|
|
900
1122
|
}
|
|
901
1123
|
|
|
902
1124
|
/**
|
|
903
1125
|
* Restarts an application.
|
|
1126
|
+
* Note: Coolify API uses GET for application start/stop/restart.
|
|
904
1127
|
*
|
|
905
1128
|
* @param appUuid - Application UUID
|
|
906
1129
|
* @returns Result with application status or error
|
|
907
1130
|
*/
|
|
908
1131
|
async restartApplication(
|
|
909
|
-
appUuid: string
|
|
1132
|
+
appUuid: string,
|
|
910
1133
|
): Promise<Result<ICoolifyApplication, Error>> {
|
|
911
|
-
log.info(`Restarting application ${appUuid}`)
|
|
1134
|
+
log.info(`Restarting application ${appUuid}`);
|
|
912
1135
|
|
|
913
|
-
const result = await this.request<ICoolifyApplication>(
|
|
914
|
-
|
|
915
|
-
|
|
1136
|
+
const result = await this.request<ICoolifyApplication>(
|
|
1137
|
+
`/applications/${appUuid}/restart`,
|
|
1138
|
+
{ method: "GET" },
|
|
1139
|
+
);
|
|
916
1140
|
|
|
917
1141
|
if (result.error) {
|
|
918
|
-
log.error(`Failed to restart application: ${result.error}`)
|
|
919
|
-
return err(new Error(result.error))
|
|
1142
|
+
log.error(`Failed to restart application: ${result.error}`);
|
|
1143
|
+
return err(new Error(result.error));
|
|
920
1144
|
}
|
|
921
1145
|
|
|
922
|
-
log.success(`Application restarted: ${appUuid}`)
|
|
923
|
-
return ok(result.data as ICoolifyApplication)
|
|
1146
|
+
log.success(`Application restarted: ${appUuid}`);
|
|
1147
|
+
return ok(result.data as ICoolifyApplication);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// ===========================================================================
|
|
1151
|
+
// Version / Health
|
|
1152
|
+
// ===========================================================================
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* Gets the Coolify server version.
|
|
1156
|
+
*
|
|
1157
|
+
* @returns Result with version info or error
|
|
1158
|
+
*/
|
|
1159
|
+
async getVersion(): Promise<Result<ICoolifyVersion, Error>> {
|
|
1160
|
+
log.info("Getting Coolify version");
|
|
1161
|
+
|
|
1162
|
+
// /version returns plain text, not JSON
|
|
1163
|
+
if (!this.baseUrl || !this.token) {
|
|
1164
|
+
return err(new Error("Coolify not configured"));
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
try {
|
|
1168
|
+
const baseUrl = this.baseUrl.replace(/\/+$/, "");
|
|
1169
|
+
const url = `${baseUrl}/api/v1/version`;
|
|
1170
|
+
const response = await fetch(url, {
|
|
1171
|
+
headers: {
|
|
1172
|
+
Authorization: `Bearer ${this.token}`,
|
|
1173
|
+
},
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
if (!response.ok) {
|
|
1177
|
+
return err(
|
|
1178
|
+
new Error(`HTTP ${response.status}: ${response.statusText}`),
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const version = (await response.text()).trim();
|
|
1183
|
+
log.success(`Coolify version: ${version}`);
|
|
1184
|
+
return ok({ version });
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1187
|
+
return err(new Error(message));
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// ===========================================================================
|
|
1192
|
+
// Database endpoints
|
|
1193
|
+
// ===========================================================================
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Lists all databases.
|
|
1197
|
+
*
|
|
1198
|
+
* @param page - Optional page number
|
|
1199
|
+
* @param perPage - Optional items per page
|
|
1200
|
+
* @returns Result with databases list or error
|
|
1201
|
+
*/
|
|
1202
|
+
async listDatabases(
|
|
1203
|
+
page?: number,
|
|
1204
|
+
perPage?: number,
|
|
1205
|
+
): Promise<Result<ICoolifyDatabase[], Error>> {
|
|
1206
|
+
log.info("Listing databases");
|
|
1207
|
+
|
|
1208
|
+
let endpoint = "/databases";
|
|
1209
|
+
const params = new URLSearchParams();
|
|
1210
|
+
if (page) params.set("page", page.toString());
|
|
1211
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
1212
|
+
if (params.toString()) endpoint += `?${params.toString()}`;
|
|
1213
|
+
|
|
1214
|
+
const result = await this.request<ICoolifyDatabase[]>(endpoint);
|
|
1215
|
+
if (result.error) return err(new Error(result.error));
|
|
1216
|
+
|
|
1217
|
+
log.success(`Listed ${result.data?.length || 0} databases`);
|
|
1218
|
+
return ok(result.data || []);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Gets details of a specific database.
|
|
1223
|
+
*
|
|
1224
|
+
* @param uuid - Database UUID
|
|
1225
|
+
* @returns Result with database details or error
|
|
1226
|
+
*/
|
|
1227
|
+
async getDatabase(uuid: string): Promise<Result<ICoolifyDatabase, Error>> {
|
|
1228
|
+
log.info(`Getting database ${uuid}`);
|
|
1229
|
+
|
|
1230
|
+
const result = await this.request<ICoolifyDatabase>(`/databases/${uuid}`);
|
|
1231
|
+
if (result.error) return err(new Error(result.error));
|
|
1232
|
+
|
|
1233
|
+
return ok(result.data as ICoolifyDatabase);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Creates a database of the specified type.
|
|
1238
|
+
*
|
|
1239
|
+
* @param dbType - Database type (postgresql, mysql, mariadb, mongodb, redis, keydb, clickhouse, dragonfly)
|
|
1240
|
+
* @param data - Database creation data
|
|
1241
|
+
* @returns Result with created database UUID or error
|
|
1242
|
+
*/
|
|
1243
|
+
async createDatabase(
|
|
1244
|
+
dbType: string,
|
|
1245
|
+
data: Record<string, unknown>,
|
|
1246
|
+
): Promise<Result<{ uuid: string }, Error>> {
|
|
1247
|
+
log.info(`Creating ${dbType} database`);
|
|
1248
|
+
|
|
1249
|
+
const result = await this.request<{ uuid: string }>(
|
|
1250
|
+
`/databases/${dbType}`,
|
|
1251
|
+
{
|
|
1252
|
+
method: "POST",
|
|
1253
|
+
body: JSON.stringify(data),
|
|
1254
|
+
},
|
|
1255
|
+
);
|
|
1256
|
+
if (result.error) return err(new Error(result.error));
|
|
1257
|
+
|
|
1258
|
+
log.success(`Database created: ${result.data?.uuid}`);
|
|
1259
|
+
return ok(result.data!);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Updates a database configuration.
|
|
1264
|
+
*
|
|
1265
|
+
* @param uuid - Database UUID
|
|
1266
|
+
* @param data - Update data
|
|
1267
|
+
* @returns Result with updated database or error
|
|
1268
|
+
*/
|
|
1269
|
+
async updateDatabase(
|
|
1270
|
+
uuid: string,
|
|
1271
|
+
data: Record<string, unknown>,
|
|
1272
|
+
): Promise<Result<ICoolifyDatabase, Error>> {
|
|
1273
|
+
log.info(`Updating database ${uuid}`);
|
|
1274
|
+
|
|
1275
|
+
const result = await this.request<ICoolifyDatabase>(`/databases/${uuid}`, {
|
|
1276
|
+
method: "PATCH",
|
|
1277
|
+
body: JSON.stringify(data),
|
|
1278
|
+
});
|
|
1279
|
+
if (result.error) return err(new Error(result.error));
|
|
1280
|
+
|
|
1281
|
+
log.success(`Database updated: ${uuid}`);
|
|
1282
|
+
return ok(result.data as ICoolifyDatabase);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Deletes a database.
|
|
1287
|
+
*
|
|
1288
|
+
* @param uuid - Database UUID
|
|
1289
|
+
* @param options - Delete options for cascade deletion
|
|
1290
|
+
* @returns Result indicating success or error
|
|
1291
|
+
*/
|
|
1292
|
+
async deleteDatabase(
|
|
1293
|
+
uuid: string,
|
|
1294
|
+
options?: {
|
|
1295
|
+
deleteConfigurations?: boolean;
|
|
1296
|
+
deleteVolumes?: boolean;
|
|
1297
|
+
dockerCleanup?: boolean;
|
|
1298
|
+
deleteConnectedNetworks?: boolean;
|
|
1299
|
+
},
|
|
1300
|
+
): Promise<Result<ICoolifyDeleteResult, Error>> {
|
|
1301
|
+
log.info(`Deleting database ${uuid}`);
|
|
1302
|
+
|
|
1303
|
+
const params = new URLSearchParams();
|
|
1304
|
+
if (options?.deleteConfigurations)
|
|
1305
|
+
params.set("delete_configurations", "true");
|
|
1306
|
+
if (options?.deleteVolumes) params.set("delete_volumes", "true");
|
|
1307
|
+
if (options?.dockerCleanup) params.set("docker_cleanup", "true");
|
|
1308
|
+
if (options?.deleteConnectedNetworks)
|
|
1309
|
+
params.set("delete_connected_networks", "true");
|
|
1310
|
+
|
|
1311
|
+
const queryString = params.toString();
|
|
1312
|
+
const endpoint = `/databases/${uuid}${queryString ? `?${queryString}` : ""}`;
|
|
1313
|
+
|
|
1314
|
+
const result = await this.request<ICoolifyDeleteResult>(endpoint, {
|
|
1315
|
+
method: "DELETE",
|
|
1316
|
+
});
|
|
1317
|
+
if (result.error) return err(new Error(result.error));
|
|
1318
|
+
|
|
1319
|
+
log.success(`Database deleted: ${uuid}`);
|
|
1320
|
+
return ok({ success: true, message: "Database deleted" });
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/**
|
|
1324
|
+
* Starts a database.
|
|
1325
|
+
*
|
|
1326
|
+
* @param uuid - Database UUID
|
|
1327
|
+
* @returns Result indicating success or error
|
|
1328
|
+
*/
|
|
1329
|
+
async startDatabase(
|
|
1330
|
+
uuid: string,
|
|
1331
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1332
|
+
log.info(`Starting database ${uuid}`);
|
|
1333
|
+
|
|
1334
|
+
const result = await this.request<{ message: string }>(
|
|
1335
|
+
`/databases/${uuid}/start`,
|
|
1336
|
+
{ method: "GET" },
|
|
1337
|
+
);
|
|
1338
|
+
if (result.error) return err(new Error(result.error));
|
|
1339
|
+
|
|
1340
|
+
log.success(`Database started: ${uuid}`);
|
|
1341
|
+
return ok(result.data || { message: "Database started" });
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Stops a database.
|
|
1346
|
+
*
|
|
1347
|
+
* @param uuid - Database UUID
|
|
1348
|
+
* @returns Result indicating success or error
|
|
1349
|
+
*/
|
|
1350
|
+
async stopDatabase(
|
|
1351
|
+
uuid: string,
|
|
1352
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1353
|
+
log.info(`Stopping database ${uuid}`);
|
|
1354
|
+
|
|
1355
|
+
const result = await this.request<{ message: string }>(
|
|
1356
|
+
`/databases/${uuid}/stop`,
|
|
1357
|
+
{ method: "GET" },
|
|
1358
|
+
);
|
|
1359
|
+
if (result.error) return err(new Error(result.error));
|
|
1360
|
+
|
|
1361
|
+
log.success(`Database stopped: ${uuid}`);
|
|
1362
|
+
return ok(result.data || { message: "Database stopped" });
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Restarts a database.
|
|
1367
|
+
*
|
|
1368
|
+
* @param uuid - Database UUID
|
|
1369
|
+
* @returns Result indicating success or error
|
|
1370
|
+
*/
|
|
1371
|
+
async restartDatabase(
|
|
1372
|
+
uuid: string,
|
|
1373
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1374
|
+
log.info(`Restarting database ${uuid}`);
|
|
1375
|
+
|
|
1376
|
+
const result = await this.request<{ message: string }>(
|
|
1377
|
+
`/databases/${uuid}/restart`,
|
|
1378
|
+
{ method: "GET" },
|
|
1379
|
+
);
|
|
1380
|
+
if (result.error) return err(new Error(result.error));
|
|
1381
|
+
|
|
1382
|
+
log.success(`Database restarted: ${uuid}`);
|
|
1383
|
+
return ok(result.data || { message: "Database restarted" });
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// ===========================================================================
|
|
1387
|
+
// Database Backup endpoints
|
|
1388
|
+
// ===========================================================================
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Lists backups for a database.
|
|
1392
|
+
*
|
|
1393
|
+
* @param databaseUuid - Database UUID
|
|
1394
|
+
* @returns Result with backups list or error
|
|
1395
|
+
*/
|
|
1396
|
+
async listDatabaseBackups(
|
|
1397
|
+
databaseUuid: string,
|
|
1398
|
+
): Promise<Result<ICoolifyDatabaseBackup[], Error>> {
|
|
1399
|
+
log.info(`Listing backups for database ${databaseUuid}`);
|
|
1400
|
+
|
|
1401
|
+
const result = await this.request<ICoolifyDatabaseBackup[]>(
|
|
1402
|
+
`/databases/${databaseUuid}/backups`,
|
|
1403
|
+
);
|
|
1404
|
+
if (result.error) return err(new Error(result.error));
|
|
1405
|
+
|
|
1406
|
+
return ok(result.data || []);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Gets a specific database backup.
|
|
1411
|
+
*
|
|
1412
|
+
* @param databaseUuid - Database UUID
|
|
1413
|
+
* @param backupUuid - Backup UUID
|
|
1414
|
+
* @returns Result with backup details or error
|
|
1415
|
+
*/
|
|
1416
|
+
async getDatabaseBackup(
|
|
1417
|
+
databaseUuid: string,
|
|
1418
|
+
backupUuid: string,
|
|
1419
|
+
): Promise<Result<ICoolifyDatabaseBackup, Error>> {
|
|
1420
|
+
const result = await this.request<ICoolifyDatabaseBackup>(
|
|
1421
|
+
`/databases/${databaseUuid}/backups/${backupUuid}`,
|
|
1422
|
+
);
|
|
1423
|
+
if (result.error) return err(new Error(result.error));
|
|
1424
|
+
|
|
1425
|
+
return ok(result.data as ICoolifyDatabaseBackup);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Creates a database backup.
|
|
1430
|
+
*
|
|
1431
|
+
* @param databaseUuid - Database UUID
|
|
1432
|
+
* @param data - Backup creation data
|
|
1433
|
+
* @returns Result with created backup or error
|
|
1434
|
+
*/
|
|
1435
|
+
async createDatabaseBackup(
|
|
1436
|
+
databaseUuid: string,
|
|
1437
|
+
data: Record<string, unknown>,
|
|
1438
|
+
): Promise<Result<ICoolifyDatabaseBackup, Error>> {
|
|
1439
|
+
log.info(`Creating backup for database ${databaseUuid}`);
|
|
1440
|
+
|
|
1441
|
+
const result = await this.request<ICoolifyDatabaseBackup>(
|
|
1442
|
+
`/databases/${databaseUuid}/backups`,
|
|
1443
|
+
{ method: "POST", body: JSON.stringify(data) },
|
|
1444
|
+
);
|
|
1445
|
+
if (result.error) return err(new Error(result.error));
|
|
1446
|
+
|
|
1447
|
+
log.success("Database backup created");
|
|
1448
|
+
return ok(result.data as ICoolifyDatabaseBackup);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/**
|
|
1452
|
+
* Updates a database backup.
|
|
1453
|
+
*
|
|
1454
|
+
* @param databaseUuid - Database UUID
|
|
1455
|
+
* @param backupUuid - Backup UUID
|
|
1456
|
+
* @param data - Update data
|
|
1457
|
+
* @returns Result indicating success or error
|
|
1458
|
+
*/
|
|
1459
|
+
async updateDatabaseBackup(
|
|
1460
|
+
databaseUuid: string,
|
|
1461
|
+
backupUuid: string,
|
|
1462
|
+
data: Record<string, unknown>,
|
|
1463
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1464
|
+
const result = await this.request<{ message: string }>(
|
|
1465
|
+
`/databases/${databaseUuid}/backups/${backupUuid}`,
|
|
1466
|
+
{ method: "PATCH", body: JSON.stringify(data) },
|
|
1467
|
+
);
|
|
1468
|
+
if (result.error) return err(new Error(result.error));
|
|
1469
|
+
|
|
1470
|
+
return ok(result.data || { message: "Backup updated" });
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
/**
|
|
1474
|
+
* Deletes a database backup.
|
|
1475
|
+
*
|
|
1476
|
+
* @param databaseUuid - Database UUID
|
|
1477
|
+
* @param backupUuid - Backup UUID
|
|
1478
|
+
* @returns Result indicating success or error
|
|
1479
|
+
*/
|
|
1480
|
+
async deleteDatabaseBackup(
|
|
1481
|
+
databaseUuid: string,
|
|
1482
|
+
backupUuid: string,
|
|
1483
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1484
|
+
const result = await this.request<{ message: string }>(
|
|
1485
|
+
`/databases/${databaseUuid}/backups/${backupUuid}`,
|
|
1486
|
+
{ method: "DELETE" },
|
|
1487
|
+
);
|
|
1488
|
+
if (result.error) return err(new Error(result.error));
|
|
1489
|
+
|
|
1490
|
+
return ok(result.data || { message: "Backup deleted" });
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// ===========================================================================
|
|
1494
|
+
// Service endpoints
|
|
1495
|
+
// ===========================================================================
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* Lists all services.
|
|
1499
|
+
*
|
|
1500
|
+
* @param page - Optional page number
|
|
1501
|
+
* @param perPage - Optional items per page
|
|
1502
|
+
* @returns Result with services list or error
|
|
1503
|
+
*/
|
|
1504
|
+
async listServices(
|
|
1505
|
+
page?: number,
|
|
1506
|
+
perPage?: number,
|
|
1507
|
+
): Promise<Result<ICoolifyServiceType[], Error>> {
|
|
1508
|
+
log.info("Listing services");
|
|
1509
|
+
|
|
1510
|
+
let endpoint = "/services";
|
|
1511
|
+
const params = new URLSearchParams();
|
|
1512
|
+
if (page) params.set("page", page.toString());
|
|
1513
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
1514
|
+
if (params.toString()) endpoint += `?${params.toString()}`;
|
|
1515
|
+
|
|
1516
|
+
const result = await this.request<ICoolifyServiceType[]>(endpoint);
|
|
1517
|
+
if (result.error) return err(new Error(result.error));
|
|
1518
|
+
|
|
1519
|
+
log.success(`Listed ${result.data?.length || 0} services`);
|
|
1520
|
+
return ok(result.data || []);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/**
|
|
1524
|
+
* Gets details of a specific service.
|
|
1525
|
+
*
|
|
1526
|
+
* @param uuid - Service UUID
|
|
1527
|
+
* @returns Result with service details or error
|
|
1528
|
+
*/
|
|
1529
|
+
async getService(uuid: string): Promise<Result<ICoolifyServiceType, Error>> {
|
|
1530
|
+
log.info(`Getting service ${uuid}`);
|
|
1531
|
+
|
|
1532
|
+
const result = await this.request<ICoolifyServiceType>(`/services/${uuid}`);
|
|
1533
|
+
if (result.error) return err(new Error(result.error));
|
|
1534
|
+
|
|
1535
|
+
return ok(result.data as ICoolifyServiceType);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Creates a new service.
|
|
1540
|
+
*
|
|
1541
|
+
* @param data - Service creation data
|
|
1542
|
+
* @returns Result with created service or error
|
|
1543
|
+
*/
|
|
1544
|
+
async createService(
|
|
1545
|
+
data: Record<string, unknown>,
|
|
1546
|
+
): Promise<Result<{ uuid: string }, Error>> {
|
|
1547
|
+
log.info("Creating service");
|
|
1548
|
+
|
|
1549
|
+
const result = await this.request<{ uuid: string }>("/services", {
|
|
1550
|
+
method: "POST",
|
|
1551
|
+
body: JSON.stringify(data),
|
|
1552
|
+
});
|
|
1553
|
+
if (result.error) return err(new Error(result.error));
|
|
1554
|
+
|
|
1555
|
+
log.success(`Service created: ${result.data?.uuid}`);
|
|
1556
|
+
return ok(result.data!);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* Updates a service configuration.
|
|
1561
|
+
*
|
|
1562
|
+
* @param uuid - Service UUID
|
|
1563
|
+
* @param data - Update data
|
|
1564
|
+
* @returns Result with updated service or error
|
|
1565
|
+
*/
|
|
1566
|
+
async updateService(
|
|
1567
|
+
uuid: string,
|
|
1568
|
+
data: Record<string, unknown>,
|
|
1569
|
+
): Promise<Result<ICoolifyServiceType, Error>> {
|
|
1570
|
+
log.info(`Updating service ${uuid}`);
|
|
1571
|
+
|
|
1572
|
+
const result = await this.request<ICoolifyServiceType>(
|
|
1573
|
+
`/services/${uuid}`,
|
|
1574
|
+
{
|
|
1575
|
+
method: "PATCH",
|
|
1576
|
+
body: JSON.stringify(data),
|
|
1577
|
+
},
|
|
1578
|
+
);
|
|
1579
|
+
if (result.error) return err(new Error(result.error));
|
|
1580
|
+
|
|
1581
|
+
log.success(`Service updated: ${uuid}`);
|
|
1582
|
+
return ok(result.data as ICoolifyServiceType);
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/**
|
|
1586
|
+
* Deletes a service.
|
|
1587
|
+
*
|
|
1588
|
+
* @param uuid - Service UUID
|
|
1589
|
+
* @param options - Delete options for cascade deletion
|
|
1590
|
+
* @returns Result indicating success or error
|
|
1591
|
+
*/
|
|
1592
|
+
async deleteService(
|
|
1593
|
+
uuid: string,
|
|
1594
|
+
options?: {
|
|
1595
|
+
deleteConfigurations?: boolean;
|
|
1596
|
+
deleteVolumes?: boolean;
|
|
1597
|
+
dockerCleanup?: boolean;
|
|
1598
|
+
deleteConnectedNetworks?: boolean;
|
|
1599
|
+
},
|
|
1600
|
+
): Promise<Result<ICoolifyDeleteResult, Error>> {
|
|
1601
|
+
log.info(`Deleting service ${uuid}`);
|
|
1602
|
+
|
|
1603
|
+
const params = new URLSearchParams();
|
|
1604
|
+
if (options?.deleteConfigurations)
|
|
1605
|
+
params.set("delete_configurations", "true");
|
|
1606
|
+
if (options?.deleteVolumes) params.set("delete_volumes", "true");
|
|
1607
|
+
if (options?.dockerCleanup) params.set("docker_cleanup", "true");
|
|
1608
|
+
if (options?.deleteConnectedNetworks)
|
|
1609
|
+
params.set("delete_connected_networks", "true");
|
|
1610
|
+
|
|
1611
|
+
const queryString = params.toString();
|
|
1612
|
+
const endpoint = `/services/${uuid}${queryString ? `?${queryString}` : ""}`;
|
|
1613
|
+
|
|
1614
|
+
const result = await this.request<ICoolifyDeleteResult>(endpoint, {
|
|
1615
|
+
method: "DELETE",
|
|
1616
|
+
});
|
|
1617
|
+
if (result.error) return err(new Error(result.error));
|
|
1618
|
+
|
|
1619
|
+
log.success(`Service deleted: ${uuid}`);
|
|
1620
|
+
return ok({ success: true, message: "Service deleted" });
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Starts a service.
|
|
1625
|
+
* Note: Coolify API uses GET for service start/stop/restart.
|
|
1626
|
+
*
|
|
1627
|
+
* @param uuid - Service UUID
|
|
1628
|
+
* @returns Result indicating success or error
|
|
1629
|
+
*/
|
|
1630
|
+
async startService(
|
|
1631
|
+
uuid: string,
|
|
1632
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1633
|
+
log.info(`Starting service ${uuid}`);
|
|
1634
|
+
|
|
1635
|
+
const result = await this.request<{ message: string }>(
|
|
1636
|
+
`/services/${uuid}/start`,
|
|
1637
|
+
{ method: "GET" },
|
|
1638
|
+
);
|
|
1639
|
+
if (result.error) return err(new Error(result.error));
|
|
1640
|
+
|
|
1641
|
+
log.success(`Service started: ${uuid}`);
|
|
1642
|
+
return ok(result.data || { message: "Service started" });
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* Stops a service.
|
|
1647
|
+
* Note: Coolify API uses GET for service start/stop/restart.
|
|
1648
|
+
*
|
|
1649
|
+
* @param uuid - Service UUID
|
|
1650
|
+
* @returns Result indicating success or error
|
|
1651
|
+
*/
|
|
1652
|
+
async stopService(uuid: string): Promise<Result<{ message: string }, Error>> {
|
|
1653
|
+
log.info(`Stopping service ${uuid}`);
|
|
1654
|
+
|
|
1655
|
+
const result = await this.request<{ message: string }>(
|
|
1656
|
+
`/services/${uuid}/stop`,
|
|
1657
|
+
{ method: "GET" },
|
|
1658
|
+
);
|
|
1659
|
+
if (result.error) return err(new Error(result.error));
|
|
1660
|
+
|
|
1661
|
+
log.success(`Service stopped: ${uuid}`);
|
|
1662
|
+
return ok(result.data || { message: "Service stopped" });
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Restarts a service.
|
|
1667
|
+
* Note: Coolify API uses GET for service start/stop/restart.
|
|
1668
|
+
*
|
|
1669
|
+
* @param uuid - Service UUID
|
|
1670
|
+
* @returns Result indicating success or error
|
|
1671
|
+
*/
|
|
1672
|
+
async restartService(
|
|
1673
|
+
uuid: string,
|
|
1674
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1675
|
+
log.info(`Restarting service ${uuid}`);
|
|
1676
|
+
|
|
1677
|
+
const result = await this.request<{ message: string }>(
|
|
1678
|
+
`/services/${uuid}/restart`,
|
|
1679
|
+
{ method: "GET" },
|
|
1680
|
+
);
|
|
1681
|
+
if (result.error) return err(new Error(result.error));
|
|
1682
|
+
|
|
1683
|
+
log.success(`Service restarted: ${uuid}`);
|
|
1684
|
+
return ok(result.data || { message: "Service restarted" });
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Lists environment variables for a service.
|
|
1689
|
+
*
|
|
1690
|
+
* @param uuid - Service UUID
|
|
1691
|
+
* @returns Result with env vars list or error
|
|
1692
|
+
*/
|
|
1693
|
+
async listServiceEnvVars(
|
|
1694
|
+
uuid: string,
|
|
1695
|
+
): Promise<Result<ICoolifyEnvVar[], Error>> {
|
|
1696
|
+
const result = await this.request<ICoolifyEnvVar[]>(
|
|
1697
|
+
`/services/${uuid}/envs`,
|
|
1698
|
+
);
|
|
1699
|
+
if (result.error) return err(new Error(result.error));
|
|
1700
|
+
|
|
1701
|
+
return ok(result.data || []);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* Creates an environment variable for a service.
|
|
1706
|
+
*
|
|
1707
|
+
* @param uuid - Service UUID
|
|
1708
|
+
* @param data - Env var data (key, value, is_preview)
|
|
1709
|
+
* @returns Result with created env var UUID or error
|
|
1710
|
+
*/
|
|
1711
|
+
async createServiceEnvVar(
|
|
1712
|
+
uuid: string,
|
|
1713
|
+
data: { key: string; value: string; is_preview?: boolean },
|
|
1714
|
+
): Promise<Result<{ uuid: string }, Error>> {
|
|
1715
|
+
const result = await this.request<{ uuid: string }>(
|
|
1716
|
+
`/services/${uuid}/envs`,
|
|
1717
|
+
{ method: "POST", body: JSON.stringify(data) },
|
|
1718
|
+
);
|
|
1719
|
+
if (result.error) return err(new Error(result.error));
|
|
1720
|
+
|
|
1721
|
+
return ok(result.data!);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
// ===========================================================================
|
|
1725
|
+
// Additional Server endpoints
|
|
1726
|
+
// ===========================================================================
|
|
1727
|
+
|
|
1728
|
+
/**
|
|
1729
|
+
* Gets resources deployed on a server.
|
|
1730
|
+
*
|
|
1731
|
+
* @param serverUuid - Server UUID
|
|
1732
|
+
* @returns Result with server resources or error
|
|
1733
|
+
*/
|
|
1734
|
+
async getServerResources(
|
|
1735
|
+
serverUuid: string,
|
|
1736
|
+
): Promise<Result<ICoolifyServerResource[], Error>> {
|
|
1737
|
+
log.info(`Getting resources for server ${serverUuid}`);
|
|
1738
|
+
|
|
1739
|
+
const result = await this.request<ICoolifyServerResource[]>(
|
|
1740
|
+
`/servers/${serverUuid}/resources`,
|
|
1741
|
+
);
|
|
1742
|
+
if (result.error) return err(new Error(result.error));
|
|
1743
|
+
|
|
1744
|
+
return ok(result.data || []);
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/**
|
|
1748
|
+
* Gets domains configured on a server.
|
|
1749
|
+
*
|
|
1750
|
+
* @param serverUuid - Server UUID
|
|
1751
|
+
* @returns Result with server domains or error
|
|
1752
|
+
*/
|
|
1753
|
+
async getServerDomains(
|
|
1754
|
+
serverUuid: string,
|
|
1755
|
+
): Promise<Result<ICoolifyServerDomain[], Error>> {
|
|
1756
|
+
log.info(`Getting domains for server ${serverUuid}`);
|
|
1757
|
+
|
|
1758
|
+
const result = await this.request<ICoolifyServerDomain[]>(
|
|
1759
|
+
`/servers/${serverUuid}/domains`,
|
|
1760
|
+
);
|
|
1761
|
+
if (result.error) return err(new Error(result.error));
|
|
1762
|
+
|
|
1763
|
+
return ok(result.data || []);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Validates a server connection.
|
|
1768
|
+
*
|
|
1769
|
+
* @param serverUuid - Server UUID
|
|
1770
|
+
* @returns Result with validation status or error
|
|
1771
|
+
*/
|
|
1772
|
+
async validateServer(
|
|
1773
|
+
serverUuid: string,
|
|
1774
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1775
|
+
log.info(`Validating server ${serverUuid}`);
|
|
1776
|
+
|
|
1777
|
+
const result = await this.request<{ message: string }>(
|
|
1778
|
+
`/servers/${serverUuid}/validate`,
|
|
1779
|
+
);
|
|
1780
|
+
if (result.error) return err(new Error(result.error));
|
|
1781
|
+
|
|
1782
|
+
return ok(result.data || { message: "Server validated" });
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
/**
|
|
1786
|
+
* Creates a new server.
|
|
1787
|
+
*
|
|
1788
|
+
* @param data - Server creation data
|
|
1789
|
+
* @returns Result with created server UUID or error
|
|
1790
|
+
*/
|
|
1791
|
+
async createServer(
|
|
1792
|
+
data: Record<string, unknown>,
|
|
1793
|
+
): Promise<Result<{ uuid: string }, Error>> {
|
|
1794
|
+
log.info("Creating server");
|
|
1795
|
+
|
|
1796
|
+
const result = await this.request<{ uuid: string }>("/servers", {
|
|
1797
|
+
method: "POST",
|
|
1798
|
+
body: JSON.stringify(data),
|
|
1799
|
+
});
|
|
1800
|
+
if (result.error) return err(new Error(result.error));
|
|
1801
|
+
|
|
1802
|
+
log.success(`Server created: ${result.data?.uuid}`);
|
|
1803
|
+
return ok(result.data!);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/**
|
|
1807
|
+
* Deletes a server.
|
|
1808
|
+
*
|
|
1809
|
+
* @param serverUuid - Server UUID
|
|
1810
|
+
* @returns Result indicating success or error
|
|
1811
|
+
*/
|
|
1812
|
+
async deleteServer(
|
|
1813
|
+
serverUuid: string,
|
|
1814
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1815
|
+
log.info(`Deleting server ${serverUuid}`);
|
|
1816
|
+
|
|
1817
|
+
const result = await this.request<{ message: string }>(
|
|
1818
|
+
`/servers/${serverUuid}`,
|
|
1819
|
+
{ method: "DELETE" },
|
|
1820
|
+
);
|
|
1821
|
+
if (result.error) return err(new Error(result.error));
|
|
1822
|
+
|
|
1823
|
+
log.success(`Server deleted: ${serverUuid}`);
|
|
1824
|
+
return ok(result.data || { message: "Server deleted" });
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// ===========================================================================
|
|
1828
|
+
// Additional Project endpoints
|
|
1829
|
+
// ===========================================================================
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Updates a project.
|
|
1833
|
+
*
|
|
1834
|
+
* @param uuid - Project UUID
|
|
1835
|
+
* @param data - Update data (name, description)
|
|
1836
|
+
* @returns Result with updated project or error
|
|
1837
|
+
*/
|
|
1838
|
+
async updateProject(
|
|
1839
|
+
uuid: string,
|
|
1840
|
+
data: { name?: string; description?: string },
|
|
1841
|
+
): Promise<Result<ICoolifyProject, Error>> {
|
|
1842
|
+
log.info(`Updating project ${uuid}`);
|
|
1843
|
+
|
|
1844
|
+
const result = await this.request<ICoolifyProject>(`/projects/${uuid}`, {
|
|
1845
|
+
method: "PATCH",
|
|
1846
|
+
body: JSON.stringify(data),
|
|
1847
|
+
});
|
|
1848
|
+
if (result.error) return err(new Error(result.error));
|
|
1849
|
+
|
|
1850
|
+
log.success(`Project updated: ${uuid}`);
|
|
1851
|
+
return ok(result.data as ICoolifyProject);
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* Deletes a project.
|
|
1856
|
+
*
|
|
1857
|
+
* @param uuid - Project UUID
|
|
1858
|
+
* @returns Result indicating success or error
|
|
1859
|
+
*/
|
|
1860
|
+
async deleteProject(
|
|
1861
|
+
uuid: string,
|
|
1862
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1863
|
+
log.info(`Deleting project ${uuid}`);
|
|
1864
|
+
|
|
1865
|
+
const result = await this.request<{ message: string }>(
|
|
1866
|
+
`/projects/${uuid}`,
|
|
1867
|
+
{ method: "DELETE" },
|
|
1868
|
+
);
|
|
1869
|
+
if (result.error) return err(new Error(result.error));
|
|
1870
|
+
|
|
1871
|
+
log.success(`Project deleted: ${uuid}`);
|
|
1872
|
+
return ok(result.data || { message: "Project deleted" });
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
/**
|
|
1876
|
+
* Creates a new environment within a project.
|
|
1877
|
+
*
|
|
1878
|
+
* @param projectUuid - Project UUID
|
|
1879
|
+
* @param data - Environment creation data (name, description)
|
|
1880
|
+
* @returns Result with created environment UUID or error
|
|
1881
|
+
*/
|
|
1882
|
+
async createProjectEnvironment(
|
|
1883
|
+
projectUuid: string,
|
|
1884
|
+
data: { name: string; description?: string },
|
|
1885
|
+
): Promise<Result<{ uuid: string }, Error>> {
|
|
1886
|
+
log.info(`Creating environment in project ${projectUuid}`);
|
|
1887
|
+
|
|
1888
|
+
const result = await this.request<{ uuid: string }>(
|
|
1889
|
+
`/projects/${projectUuid}/environments`,
|
|
1890
|
+
{ method: "POST", body: JSON.stringify(data) },
|
|
1891
|
+
);
|
|
1892
|
+
if (result.error) return err(new Error(result.error));
|
|
1893
|
+
|
|
1894
|
+
log.success("Environment created");
|
|
1895
|
+
return ok(result.data!);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// ===========================================================================
|
|
1899
|
+
// Additional Team endpoints
|
|
1900
|
+
// ===========================================================================
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* Gets the current team.
|
|
1904
|
+
*
|
|
1905
|
+
* @returns Result with current team or error
|
|
1906
|
+
*/
|
|
1907
|
+
async getCurrentTeam(): Promise<Result<ICoolifyTeam, Error>> {
|
|
1908
|
+
log.info("Getting current team");
|
|
1909
|
+
|
|
1910
|
+
const result = await this.request<ICoolifyTeam>("/teams/current");
|
|
1911
|
+
if (result.error) return err(new Error(result.error));
|
|
1912
|
+
|
|
1913
|
+
return ok(result.data as ICoolifyTeam);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
/**
|
|
1917
|
+
* Gets a specific team by ID.
|
|
1918
|
+
*
|
|
1919
|
+
* @param id - Team ID
|
|
1920
|
+
* @returns Result with team details or error
|
|
1921
|
+
*/
|
|
1922
|
+
async getTeam(id: number): Promise<Result<ICoolifyTeam, Error>> {
|
|
1923
|
+
log.info(`Getting team ${id}`);
|
|
1924
|
+
|
|
1925
|
+
const result = await this.request<ICoolifyTeam>(`/teams/${id}`);
|
|
1926
|
+
if (result.error) return err(new Error(result.error));
|
|
1927
|
+
|
|
1928
|
+
return ok(result.data as ICoolifyTeam);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Gets members of a specific team.
|
|
1933
|
+
*
|
|
1934
|
+
* @param id - Team ID
|
|
1935
|
+
* @returns Result with team members or error
|
|
1936
|
+
*/
|
|
1937
|
+
async getTeamMembers(
|
|
1938
|
+
id: number,
|
|
1939
|
+
): Promise<
|
|
1940
|
+
Result<Array<{ id: number; name: string; email: string }>, Error>
|
|
1941
|
+
> {
|
|
1942
|
+
log.info(`Getting members for team ${id}`);
|
|
1943
|
+
|
|
1944
|
+
const result = await this.request<
|
|
1945
|
+
Array<{ id: number; name: string; email: string }>
|
|
1946
|
+
>(`/teams/${id}/members`);
|
|
1947
|
+
if (result.error) return err(new Error(result.error));
|
|
1948
|
+
|
|
1949
|
+
return ok(result.data || []);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// ===========================================================================
|
|
1953
|
+
// Deployment Control
|
|
1954
|
+
// ===========================================================================
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Cancels a deployment.
|
|
1958
|
+
*
|
|
1959
|
+
* @param deploymentUuid - Deployment UUID
|
|
1960
|
+
* @returns Result indicating success or error
|
|
1961
|
+
*/
|
|
1962
|
+
async cancelDeployment(
|
|
1963
|
+
deploymentUuid: string,
|
|
1964
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
1965
|
+
log.info(`Cancelling deployment ${deploymentUuid}`);
|
|
1966
|
+
|
|
1967
|
+
const result = await this.request<{ message: string }>(
|
|
1968
|
+
`/deployments/${deploymentUuid}/cancel`,
|
|
1969
|
+
{ method: "POST" },
|
|
1970
|
+
);
|
|
1971
|
+
if (result.error) return err(new Error(result.error));
|
|
1972
|
+
|
|
1973
|
+
log.success(`Deployment cancelled: ${deploymentUuid}`);
|
|
1974
|
+
return ok(result.data || { message: "Deployment cancelled" });
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// ===========================================================================
|
|
1978
|
+
// SSH / Private Key endpoints
|
|
1979
|
+
// ===========================================================================
|
|
1980
|
+
|
|
1981
|
+
/**
|
|
1982
|
+
* Lists all private keys.
|
|
1983
|
+
*
|
|
1984
|
+
* @returns Result with private keys list or error
|
|
1985
|
+
*/
|
|
1986
|
+
async listPrivateKeys(): Promise<Result<ICoolifyPrivateKey[], Error>> {
|
|
1987
|
+
log.info("Listing private keys");
|
|
1988
|
+
|
|
1989
|
+
const result = await this.request<ICoolifyPrivateKey[]>("/security/keys");
|
|
1990
|
+
if (result.error) return err(new Error(result.error));
|
|
1991
|
+
|
|
1992
|
+
return ok(result.data || []);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
/**
|
|
1996
|
+
* Gets a specific private key.
|
|
1997
|
+
*
|
|
1998
|
+
* @param uuid - Private key UUID
|
|
1999
|
+
* @returns Result with private key details or error
|
|
2000
|
+
*/
|
|
2001
|
+
async getPrivateKey(
|
|
2002
|
+
uuid: string,
|
|
2003
|
+
): Promise<Result<ICoolifyPrivateKey, Error>> {
|
|
2004
|
+
const result = await this.request<ICoolifyPrivateKey>(
|
|
2005
|
+
`/security/keys/${uuid}`,
|
|
2006
|
+
);
|
|
2007
|
+
if (result.error) return err(new Error(result.error));
|
|
2008
|
+
|
|
2009
|
+
return ok(result.data as ICoolifyPrivateKey);
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
/**
|
|
2013
|
+
* Creates a new private key.
|
|
2014
|
+
*
|
|
2015
|
+
* @param data - Key creation data (name, private_key, description)
|
|
2016
|
+
* @returns Result with created key UUID or error
|
|
2017
|
+
*/
|
|
2018
|
+
async createPrivateKey(data: {
|
|
2019
|
+
name: string;
|
|
2020
|
+
private_key: string;
|
|
2021
|
+
description?: string;
|
|
2022
|
+
}): Promise<Result<{ uuid: string }, Error>> {
|
|
2023
|
+
log.info("Creating private key");
|
|
2024
|
+
|
|
2025
|
+
const result = await this.request<{ uuid: string }>("/security/keys", {
|
|
2026
|
+
method: "POST",
|
|
2027
|
+
body: JSON.stringify(data),
|
|
2028
|
+
});
|
|
2029
|
+
if (result.error) return err(new Error(result.error));
|
|
2030
|
+
|
|
2031
|
+
log.success(`Private key created: ${result.data?.uuid}`);
|
|
2032
|
+
return ok(result.data!);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* Updates a private key.
|
|
2037
|
+
*
|
|
2038
|
+
* @param uuid - Private key UUID
|
|
2039
|
+
* @param data - Update data
|
|
2040
|
+
* @returns Result with updated key or error
|
|
2041
|
+
*/
|
|
2042
|
+
async updatePrivateKey(
|
|
2043
|
+
uuid: string,
|
|
2044
|
+
data: { name?: string; private_key?: string; description?: string },
|
|
2045
|
+
): Promise<Result<ICoolifyPrivateKey, Error>> {
|
|
2046
|
+
const result = await this.request<ICoolifyPrivateKey>(
|
|
2047
|
+
`/security/keys/${uuid}`,
|
|
2048
|
+
{ method: "PATCH", body: JSON.stringify(data) },
|
|
2049
|
+
);
|
|
2050
|
+
if (result.error) return err(new Error(result.error));
|
|
2051
|
+
|
|
2052
|
+
return ok(result.data as ICoolifyPrivateKey);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
/**
|
|
2056
|
+
* Deletes a private key.
|
|
2057
|
+
*
|
|
2058
|
+
* @param uuid - Private key UUID
|
|
2059
|
+
* @returns Result indicating success or error
|
|
2060
|
+
*/
|
|
2061
|
+
async deletePrivateKey(
|
|
2062
|
+
uuid: string,
|
|
2063
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
2064
|
+
log.info(`Deleting private key ${uuid}`);
|
|
2065
|
+
|
|
2066
|
+
const result = await this.request<{ message: string }>(
|
|
2067
|
+
`/security/keys/${uuid}`,
|
|
2068
|
+
{ method: "DELETE" },
|
|
2069
|
+
);
|
|
2070
|
+
if (result.error) return err(new Error(result.error));
|
|
2071
|
+
|
|
2072
|
+
log.success(`Private key deleted: ${uuid}`);
|
|
2073
|
+
return ok(result.data || { message: "Private key deleted" });
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// ===========================================================================
|
|
2077
|
+
// GitHub App endpoints (additional)
|
|
2078
|
+
// ===========================================================================
|
|
2079
|
+
|
|
2080
|
+
/**
|
|
2081
|
+
* Creates a GitHub App configuration.
|
|
2082
|
+
*
|
|
2083
|
+
* @param data - GitHub App creation data
|
|
2084
|
+
* @returns Result with created app or error
|
|
2085
|
+
*/
|
|
2086
|
+
async createGitHubApp(
|
|
2087
|
+
data: Record<string, unknown>,
|
|
2088
|
+
): Promise<Result<{ id: number; uuid: string }, Error>> {
|
|
2089
|
+
log.info("Creating GitHub App");
|
|
2090
|
+
|
|
2091
|
+
const result = await this.request<{ id: number; uuid: string }>(
|
|
2092
|
+
"/github-apps",
|
|
2093
|
+
{ method: "POST", body: JSON.stringify(data) },
|
|
2094
|
+
);
|
|
2095
|
+
if (result.error) return err(new Error(result.error));
|
|
2096
|
+
|
|
2097
|
+
return ok(result.data!);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
/**
|
|
2101
|
+
* Updates a GitHub App configuration.
|
|
2102
|
+
*
|
|
2103
|
+
* @param id - GitHub App ID
|
|
2104
|
+
* @param data - Update data
|
|
2105
|
+
* @returns Result with updated app or error
|
|
2106
|
+
*/
|
|
2107
|
+
async updateGitHubApp(
|
|
2108
|
+
id: number,
|
|
2109
|
+
data: Record<string, unknown>,
|
|
2110
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
2111
|
+
const result = await this.request<{ message: string }>(
|
|
2112
|
+
`/github-apps/${id}`,
|
|
2113
|
+
{ method: "PATCH", body: JSON.stringify(data) },
|
|
2114
|
+
);
|
|
2115
|
+
if (result.error) return err(new Error(result.error));
|
|
2116
|
+
|
|
2117
|
+
return ok(result.data || { message: "GitHub App updated" });
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Deletes a GitHub App configuration.
|
|
2122
|
+
*
|
|
2123
|
+
* @param id - GitHub App ID
|
|
2124
|
+
* @returns Result indicating success or error
|
|
2125
|
+
*/
|
|
2126
|
+
async deleteGitHubApp(
|
|
2127
|
+
id: number,
|
|
2128
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
2129
|
+
log.info(`Deleting GitHub App ${id}`);
|
|
2130
|
+
|
|
2131
|
+
const result = await this.request<{ message: string }>(
|
|
2132
|
+
`/github-apps/${id}`,
|
|
2133
|
+
{ method: "DELETE" },
|
|
2134
|
+
);
|
|
2135
|
+
if (result.error) return err(new Error(result.error));
|
|
2136
|
+
|
|
2137
|
+
return ok(result.data || { message: "GitHub App deleted" });
|
|
924
2138
|
}
|
|
925
2139
|
|
|
926
2140
|
/**
|
|
927
2141
|
* Lists all active and queued deployments.
|
|
928
2142
|
*
|
|
2143
|
+
* @param page - Optional page number for pagination
|
|
2144
|
+
* @param perPage - Optional number of items per page
|
|
929
2145
|
* @returns Result with deployments list or error
|
|
930
2146
|
*/
|
|
931
|
-
async listDeployments(
|
|
932
|
-
|
|
2147
|
+
async listDeployments(
|
|
2148
|
+
page?: number,
|
|
2149
|
+
perPage?: number,
|
|
2150
|
+
): Promise<Result<ICoolifyDeployment[], Error>> {
|
|
2151
|
+
log.info("Listing active deployments");
|
|
2152
|
+
|
|
2153
|
+
let endpoint = "/deployments";
|
|
2154
|
+
const params = new URLSearchParams();
|
|
2155
|
+
if (page) params.set("page", page.toString());
|
|
2156
|
+
if (perPage) params.set("per_page", perPage.toString());
|
|
2157
|
+
if (params.toString()) {
|
|
2158
|
+
endpoint += `?${params.toString()}`;
|
|
2159
|
+
}
|
|
933
2160
|
|
|
934
|
-
const result = await this.request<ICoolifyDeployment[]>(
|
|
2161
|
+
const result = await this.request<ICoolifyDeployment[]>(endpoint);
|
|
935
2162
|
|
|
936
2163
|
if (result.error) {
|
|
937
|
-
log.error(`Failed to list deployments: ${result.error}`)
|
|
938
|
-
return err(new Error(result.error))
|
|
2164
|
+
log.error(`Failed to list deployments: ${result.error}`);
|
|
2165
|
+
return err(new Error(result.error));
|
|
939
2166
|
}
|
|
940
2167
|
|
|
941
|
-
log.success(`Listed ${result.data?.length || 0} active deployments`)
|
|
942
|
-
return ok(result.data || [])
|
|
2168
|
+
log.success(`Listed ${result.data?.length || 0} active deployments`);
|
|
2169
|
+
return ok(result.data || []);
|
|
943
2170
|
}
|
|
944
2171
|
|
|
945
2172
|
/**
|
|
@@ -949,19 +2176,53 @@ export class CoolifyService {
|
|
|
949
2176
|
* @returns Result with deployment details or error
|
|
950
2177
|
*/
|
|
951
2178
|
async getDeployment(
|
|
952
|
-
deploymentUuid: string
|
|
2179
|
+
deploymentUuid: string,
|
|
953
2180
|
): Promise<Result<ICoolifyDeployment, Error>> {
|
|
954
|
-
log.info(`Getting deployment details for ${deploymentUuid}`)
|
|
2181
|
+
log.info(`Getting deployment details for ${deploymentUuid}`);
|
|
955
2182
|
|
|
956
|
-
const result = await this.request<ICoolifyDeployment>(
|
|
2183
|
+
const result = await this.request<ICoolifyDeployment>(
|
|
2184
|
+
`/deployments/${deploymentUuid}`,
|
|
2185
|
+
);
|
|
957
2186
|
|
|
958
2187
|
if (result.error) {
|
|
959
|
-
log.error(`Failed to get deployment: ${result.error}`)
|
|
960
|
-
return err(new Error(result.error))
|
|
2188
|
+
log.error(`Failed to get deployment: ${result.error}`);
|
|
2189
|
+
return err(new Error(result.error));
|
|
961
2190
|
}
|
|
962
2191
|
|
|
963
|
-
log.success(`Deployment details retrieved: ${deploymentUuid}`)
|
|
964
|
-
return ok(result.data as ICoolifyDeployment)
|
|
2192
|
+
log.success(`Deployment details retrieved: ${deploymentUuid}`);
|
|
2193
|
+
return ok(result.data as ICoolifyDeployment);
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
/**
|
|
2197
|
+
* Gets logs for a specific deployment.
|
|
2198
|
+
*
|
|
2199
|
+
* @param deploymentUuid - Deployment UUID
|
|
2200
|
+
* @returns Result with deployment status and logs or error
|
|
2201
|
+
*/
|
|
2202
|
+
async getDeploymentLogs(
|
|
2203
|
+
deploymentUuid: string,
|
|
2204
|
+
): Promise<
|
|
2205
|
+
Result<{ status: string; logs: string; deployment_uuid: string }, Error>
|
|
2206
|
+
> {
|
|
2207
|
+
log.info(`Getting deployment logs for ${deploymentUuid}`);
|
|
2208
|
+
|
|
2209
|
+
const result = await this.request<{
|
|
2210
|
+
status: string;
|
|
2211
|
+
logs: string;
|
|
2212
|
+
deployment_uuid: string;
|
|
2213
|
+
}>(`/deployments/${deploymentUuid}`);
|
|
2214
|
+
|
|
2215
|
+
if (result.error) {
|
|
2216
|
+
log.error(`Failed to get deployment logs: ${result.error}`);
|
|
2217
|
+
return err(new Error(result.error));
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
log.success(`Deployment logs retrieved: ${deploymentUuid}`);
|
|
2221
|
+
return ok({
|
|
2222
|
+
status: result.data?.status || "unknown",
|
|
2223
|
+
logs: result.data?.logs || "",
|
|
2224
|
+
deployment_uuid: result.data?.deployment_uuid || deploymentUuid,
|
|
2225
|
+
});
|
|
965
2226
|
}
|
|
966
2227
|
|
|
967
2228
|
/**
|
|
@@ -975,29 +2236,611 @@ export class CoolifyService {
|
|
|
975
2236
|
async getApplicationDeployments(
|
|
976
2237
|
appUuid: string,
|
|
977
2238
|
skip: number = 0,
|
|
978
|
-
take: number = 10
|
|
2239
|
+
take: number = 10,
|
|
979
2240
|
): Promise<Result<ICoolifyDeployment[], Error>> {
|
|
980
|
-
log.info(`Getting deployments for application ${appUuid}`)
|
|
2241
|
+
log.info(`Getting deployments for application ${appUuid}`);
|
|
2242
|
+
|
|
2243
|
+
const params = new URLSearchParams();
|
|
2244
|
+
if (skip > 0) params.set("skip", skip.toString());
|
|
2245
|
+
if (take !== 10) params.set("take", take.toString());
|
|
2246
|
+
|
|
2247
|
+
const endpoint = `/applications/${appUuid}/deployments${params.toString() ? `?${params.toString()}` : ""}`;
|
|
2248
|
+
const result = await this.request<{
|
|
2249
|
+
count: number;
|
|
2250
|
+
deployments: ICoolifyDeployment[];
|
|
2251
|
+
}>(endpoint);
|
|
2252
|
+
|
|
2253
|
+
if (result.error) {
|
|
2254
|
+
log.error(`Failed to get application deployments: ${result.error}`);
|
|
2255
|
+
return err(new Error(result.error));
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
const deployments = result.data?.deployments || [];
|
|
2259
|
+
log.success(`Retrieved ${deployments.length} deployments for ${appUuid}`);
|
|
2260
|
+
return ok(deployments);
|
|
2261
|
+
}
|
|
981
2262
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
2263
|
+
/**
|
|
2264
|
+
* Lists deployments for a specific application.
|
|
2265
|
+
*
|
|
2266
|
+
* Uses the /deployments/applications/{appUuid} endpoint which returns
|
|
2267
|
+
* all deployments (active, queued, and completed) for a single application.
|
|
2268
|
+
* This differs from listDeployments() which returns ALL deployments globally.
|
|
2269
|
+
*
|
|
2270
|
+
* @param appUuid - Application UUID
|
|
2271
|
+
* @returns Result with deployments list or error
|
|
2272
|
+
*/
|
|
2273
|
+
async listApplicationDeployments(
|
|
2274
|
+
appUuid: string,
|
|
2275
|
+
): Promise<Result<ICoolifyDeployment[], Error>> {
|
|
2276
|
+
log.info(`Listing deployments for application ${appUuid}`);
|
|
985
2277
|
|
|
986
|
-
|
|
987
|
-
const result = await this.request<{
|
|
2278
|
+
// API returns { count: number, deployments: ICoolifyDeployment[] }
|
|
2279
|
+
const result = await this.request<{
|
|
2280
|
+
count: number;
|
|
2281
|
+
deployments: ICoolifyDeployment[];
|
|
2282
|
+
}>(`/deployments/applications/${appUuid}`);
|
|
988
2283
|
|
|
989
2284
|
if (result.error) {
|
|
990
|
-
log.error(`Failed to
|
|
991
|
-
return err(new Error(result.error))
|
|
2285
|
+
log.error(`Failed to list application deployments: ${result.error}`);
|
|
2286
|
+
return err(new Error(result.error));
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
const deployments = result.data?.deployments || [];
|
|
2290
|
+
log.success(`Listed ${deployments.length} deployments for ${appUuid}`);
|
|
2291
|
+
return ok(deployments);
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
// ===========================================================================
|
|
2295
|
+
// Smart Resolution Helpers
|
|
2296
|
+
// ===========================================================================
|
|
2297
|
+
|
|
2298
|
+
/**
|
|
2299
|
+
* Resolves an application by UUID, name, or domain (FQDN).
|
|
2300
|
+
*
|
|
2301
|
+
* @param query - UUID, name, or domain to search for
|
|
2302
|
+
* @returns Result with application or error
|
|
2303
|
+
*/
|
|
2304
|
+
async resolveApplication(
|
|
2305
|
+
query: string,
|
|
2306
|
+
): Promise<Result<ICoolifyApplication, Error>> {
|
|
2307
|
+
log.info(`Resolving application: ${query}`);
|
|
2308
|
+
|
|
2309
|
+
// If it looks like a UUID, try direct lookup first
|
|
2310
|
+
if (this.isLikelyUuid(query)) {
|
|
2311
|
+
const apps = await this.listApplications();
|
|
2312
|
+
if (isErr(apps)) return err(apps.error);
|
|
2313
|
+
const match = apps.value.find((a) => a.uuid === query);
|
|
2314
|
+
if (match) return ok(match);
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// Search by name or domain
|
|
2318
|
+
const apps = await this.listApplications();
|
|
2319
|
+
if (isErr(apps)) return err(apps.error);
|
|
2320
|
+
|
|
2321
|
+
const lowerQuery = query.toLowerCase();
|
|
2322
|
+
const matches = apps.value.filter(
|
|
2323
|
+
(a) =>
|
|
2324
|
+
a.name?.toLowerCase() === lowerQuery ||
|
|
2325
|
+
a.fqdn?.toLowerCase().includes(lowerQuery) ||
|
|
2326
|
+
a.uuid.startsWith(query),
|
|
2327
|
+
);
|
|
2328
|
+
|
|
2329
|
+
if (matches.length === 1) return ok(matches[0]);
|
|
2330
|
+
if (matches.length === 0) {
|
|
2331
|
+
return err(
|
|
2332
|
+
new Error(
|
|
2333
|
+
`No application found matching "${query}". Use 'list' to see available applications.`,
|
|
2334
|
+
),
|
|
2335
|
+
);
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
const names = matches.map((a) => ` - ${a.name} (${a.uuid})`).join("\n");
|
|
2339
|
+
return err(
|
|
2340
|
+
new Error(
|
|
2341
|
+
`Multiple applications match "${query}":\n${names}\nPlease use the full UUID.`,
|
|
2342
|
+
),
|
|
2343
|
+
);
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
/**
|
|
2347
|
+
* Resolves a server by UUID, name, or IP address.
|
|
2348
|
+
*
|
|
2349
|
+
* @param query - UUID, name, or IP to search for
|
|
2350
|
+
* @returns Result with server or error
|
|
2351
|
+
*/
|
|
2352
|
+
async resolveServer(query: string): Promise<Result<ICoolifyServer, Error>> {
|
|
2353
|
+
log.info(`Resolving server: ${query}`);
|
|
2354
|
+
|
|
2355
|
+
const servers = await this.listServers();
|
|
2356
|
+
if (isErr(servers)) return err(servers.error);
|
|
2357
|
+
|
|
2358
|
+
const lowerQuery = query.toLowerCase();
|
|
2359
|
+
const match = servers.value.find(
|
|
2360
|
+
(s) =>
|
|
2361
|
+
s.uuid === query ||
|
|
2362
|
+
s.name?.toLowerCase() === lowerQuery ||
|
|
2363
|
+
s.ip === query ||
|
|
2364
|
+
s.uuid.startsWith(query),
|
|
2365
|
+
);
|
|
2366
|
+
|
|
2367
|
+
if (match) return ok(match);
|
|
2368
|
+
return err(
|
|
2369
|
+
new Error(
|
|
2370
|
+
`No server found matching "${query}". Use 'servers' to see available servers.`,
|
|
2371
|
+
),
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
/**
|
|
2376
|
+
* Checks if a string looks like a UUID (Coolify or standard format).
|
|
2377
|
+
*
|
|
2378
|
+
* @param query - String to check
|
|
2379
|
+
* @returns true if it looks like a UUID
|
|
2380
|
+
*/
|
|
2381
|
+
private isLikelyUuid(query: string): boolean {
|
|
2382
|
+
if (/^[a-z0-9]{20,}$/i.test(query)) return true;
|
|
2383
|
+
if (
|
|
2384
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
2385
|
+
query,
|
|
2386
|
+
)
|
|
2387
|
+
)
|
|
2388
|
+
return true;
|
|
2389
|
+
return false;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// ===========================================================================
|
|
2393
|
+
// Diagnostics
|
|
2394
|
+
// ===========================================================================
|
|
2395
|
+
|
|
2396
|
+
/**
|
|
2397
|
+
* Diagnoses an application by aggregating health, logs, deployments, and env vars.
|
|
2398
|
+
*
|
|
2399
|
+
* @param query - Application UUID, name, or domain
|
|
2400
|
+
* @returns Result with diagnostic report or error
|
|
2401
|
+
*/
|
|
2402
|
+
async diagnoseApplication(query: string): Promise<
|
|
2403
|
+
Result<
|
|
2404
|
+
{
|
|
2405
|
+
application: ICoolifyApplication;
|
|
2406
|
+
recentDeployments: ICoolifyDeployment[];
|
|
2407
|
+
envVarCount: number;
|
|
2408
|
+
recentLogs: string[];
|
|
2409
|
+
issues: string[];
|
|
2410
|
+
},
|
|
2411
|
+
Error
|
|
2412
|
+
>
|
|
2413
|
+
> {
|
|
2414
|
+
log.info(`Diagnosing application: ${query}`);
|
|
2415
|
+
|
|
2416
|
+
const appResult = await this.resolveApplication(query);
|
|
2417
|
+
if (isErr(appResult)) return err(appResult.error);
|
|
2418
|
+
|
|
2419
|
+
const app = appResult.value;
|
|
2420
|
+
const issues: string[] = [];
|
|
2421
|
+
|
|
2422
|
+
// Gather data in parallel
|
|
2423
|
+
const [deploymentsResult, envResult, logsResult] = await Promise.all([
|
|
2424
|
+
this.getApplicationDeploymentHistory(app.uuid),
|
|
2425
|
+
this.getEnvironmentVariables(app.uuid),
|
|
2426
|
+
this.getApplicationLogs(app.uuid, { tail: 20 }),
|
|
2427
|
+
]);
|
|
2428
|
+
|
|
2429
|
+
const deployments = isErr(deploymentsResult) ? [] : deploymentsResult.value;
|
|
2430
|
+
const envVarCount = isErr(envResult) ? 0 : envResult.value.length;
|
|
2431
|
+
const recentLogs = isErr(logsResult) ? [] : logsResult.value.logs;
|
|
2432
|
+
|
|
2433
|
+
// Check for issues
|
|
2434
|
+
if (app.status?.includes("stopped")) {
|
|
2435
|
+
issues.push("Application is stopped");
|
|
2436
|
+
}
|
|
2437
|
+
if (!app.fqdn) {
|
|
2438
|
+
issues.push("No domain configured");
|
|
2439
|
+
}
|
|
2440
|
+
if (envVarCount === 0) {
|
|
2441
|
+
issues.push("No environment variables set");
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
const recentDeploys = deployments.slice(-5);
|
|
2445
|
+
const failedDeploys = recentDeploys.filter((d) =>
|
|
2446
|
+
d.status?.includes("failed"),
|
|
2447
|
+
);
|
|
2448
|
+
if (failedDeploys.length > 0) {
|
|
2449
|
+
issues.push(
|
|
2450
|
+
`${failedDeploys.length} of last ${recentDeploys.length} deployments failed`,
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
log.success(`Diagnosis complete for ${app.name}`);
|
|
2455
|
+
return ok({
|
|
2456
|
+
application: app,
|
|
2457
|
+
recentDeployments: recentDeploys,
|
|
2458
|
+
envVarCount,
|
|
2459
|
+
recentLogs,
|
|
2460
|
+
issues,
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
/**
|
|
2465
|
+
* Diagnoses a server by aggregating health, resources, and domains.
|
|
2466
|
+
*
|
|
2467
|
+
* @param query - Server UUID, name, or IP
|
|
2468
|
+
* @returns Result with diagnostic report or error
|
|
2469
|
+
*/
|
|
2470
|
+
async diagnoseServer(query: string): Promise<
|
|
2471
|
+
Result<
|
|
2472
|
+
{
|
|
2473
|
+
server: ICoolifyServer;
|
|
2474
|
+
resources: ICoolifyServerResource[];
|
|
2475
|
+
domains: ICoolifyServerDomain[];
|
|
2476
|
+
issues: string[];
|
|
2477
|
+
},
|
|
2478
|
+
Error
|
|
2479
|
+
>
|
|
2480
|
+
> {
|
|
2481
|
+
log.info(`Diagnosing server: ${query}`);
|
|
2482
|
+
|
|
2483
|
+
const serverResult = await this.resolveServer(query);
|
|
2484
|
+
if (isErr(serverResult)) return err(serverResult.error);
|
|
2485
|
+
|
|
2486
|
+
const server = serverResult.value;
|
|
2487
|
+
const issues: string[] = [];
|
|
2488
|
+
|
|
2489
|
+
const [resourcesResult, domainsResult] = await Promise.all([
|
|
2490
|
+
this.getServerResources(server.uuid),
|
|
2491
|
+
this.getServerDomains(server.uuid),
|
|
2492
|
+
]);
|
|
2493
|
+
|
|
2494
|
+
const resources = isErr(resourcesResult) ? [] : resourcesResult.value;
|
|
2495
|
+
const domains = isErr(domainsResult) ? [] : domainsResult.value;
|
|
2496
|
+
|
|
2497
|
+
if (!server.is_reachable) {
|
|
2498
|
+
issues.push("Server is not reachable");
|
|
2499
|
+
}
|
|
2500
|
+
if (!server.is_usable) {
|
|
2501
|
+
issues.push("Server is not usable");
|
|
2502
|
+
}
|
|
2503
|
+
if (resources.length === 0) {
|
|
2504
|
+
issues.push("No resources deployed on this server");
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
log.success(`Server diagnosis complete for ${server.name}`);
|
|
2508
|
+
return ok({ server, resources, domains, issues });
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
/**
|
|
2512
|
+
* Scans all infrastructure for potential issues.
|
|
2513
|
+
*
|
|
2514
|
+
* @returns Result with issues report or error
|
|
2515
|
+
*/
|
|
2516
|
+
async findInfrastructureIssues(): Promise<
|
|
2517
|
+
Result<
|
|
2518
|
+
{
|
|
2519
|
+
totalServers: number;
|
|
2520
|
+
totalApps: number;
|
|
2521
|
+
totalDatabases: number;
|
|
2522
|
+
totalServices: number;
|
|
2523
|
+
issues: Array<{
|
|
2524
|
+
type: string;
|
|
2525
|
+
resource: string;
|
|
2526
|
+
uuid: string;
|
|
2527
|
+
message: string;
|
|
2528
|
+
}>;
|
|
2529
|
+
},
|
|
2530
|
+
Error
|
|
2531
|
+
>
|
|
2532
|
+
> {
|
|
2533
|
+
log.info("Scanning infrastructure for issues");
|
|
2534
|
+
|
|
2535
|
+
const [serversR, appsR, dbsR, svcsR] = await Promise.all([
|
|
2536
|
+
this.listServers(),
|
|
2537
|
+
this.listApplications(),
|
|
2538
|
+
this.listDatabases(),
|
|
2539
|
+
this.listServices(),
|
|
2540
|
+
]);
|
|
2541
|
+
|
|
2542
|
+
const servers = isErr(serversR) ? [] : serversR.value;
|
|
2543
|
+
const apps = isErr(appsR) ? [] : appsR.value;
|
|
2544
|
+
const dbs = isErr(dbsR) ? [] : dbsR.value;
|
|
2545
|
+
const svcs = isErr(svcsR) ? [] : svcsR.value;
|
|
2546
|
+
|
|
2547
|
+
const issues: Array<{
|
|
2548
|
+
type: string;
|
|
2549
|
+
resource: string;
|
|
2550
|
+
uuid: string;
|
|
2551
|
+
message: string;
|
|
2552
|
+
}> = [];
|
|
2553
|
+
|
|
2554
|
+
for (const server of servers) {
|
|
2555
|
+
if (!server.is_reachable) {
|
|
2556
|
+
issues.push({
|
|
2557
|
+
type: "server",
|
|
2558
|
+
resource: server.name,
|
|
2559
|
+
uuid: server.uuid,
|
|
2560
|
+
message: "Server unreachable",
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
for (const app of apps) {
|
|
2566
|
+
if (app.status?.includes("stopped")) {
|
|
2567
|
+
issues.push({
|
|
2568
|
+
type: "application",
|
|
2569
|
+
resource: app.name,
|
|
2570
|
+
uuid: app.uuid,
|
|
2571
|
+
message: "Application stopped",
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
if (app.status?.includes("failed") || app.status?.includes("error")) {
|
|
2575
|
+
issues.push({
|
|
2576
|
+
type: "application",
|
|
2577
|
+
resource: app.name,
|
|
2578
|
+
uuid: app.uuid,
|
|
2579
|
+
message: `Status: ${app.status}`,
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
for (const db of dbs) {
|
|
2585
|
+
if (db.status?.includes("stopped") || db.status?.includes("exited")) {
|
|
2586
|
+
issues.push({
|
|
2587
|
+
type: "database",
|
|
2588
|
+
resource: db.name,
|
|
2589
|
+
uuid: db.uuid,
|
|
2590
|
+
message: `Database stopped: ${db.status}`,
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
for (const svc of svcs) {
|
|
2596
|
+
if (svc.status?.includes("stopped") || svc.status?.includes("exited")) {
|
|
2597
|
+
issues.push({
|
|
2598
|
+
type: "service",
|
|
2599
|
+
resource: svc.name,
|
|
2600
|
+
uuid: svc.uuid,
|
|
2601
|
+
message: `Service stopped: ${svc.status}`,
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
log.success(
|
|
2607
|
+
`Infrastructure scan complete: ${issues.length} issue(s) found`,
|
|
2608
|
+
);
|
|
2609
|
+
return ok({
|
|
2610
|
+
totalServers: servers.length,
|
|
2611
|
+
totalApps: apps.length,
|
|
2612
|
+
totalDatabases: dbs.length,
|
|
2613
|
+
totalServices: svcs.length,
|
|
2614
|
+
issues,
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
// ===========================================================================
|
|
2619
|
+
// Batch Operations
|
|
2620
|
+
// ===========================================================================
|
|
2621
|
+
|
|
2622
|
+
/**
|
|
2623
|
+
* Restarts all applications in a project.
|
|
2624
|
+
*
|
|
2625
|
+
* @param projectUuid - Project UUID
|
|
2626
|
+
* @returns Result with batch operation results
|
|
2627
|
+
*/
|
|
2628
|
+
async restartProjectApps(
|
|
2629
|
+
projectUuid: string,
|
|
2630
|
+
): Promise<
|
|
2631
|
+
Result<{ total: number; succeeded: number; failed: string[] }, Error>
|
|
2632
|
+
> {
|
|
2633
|
+
log.info(`Restarting all apps in project ${projectUuid}`);
|
|
2634
|
+
|
|
2635
|
+
const appsResult = await this.listApplications(undefined, projectUuid);
|
|
2636
|
+
if (isErr(appsResult)) return err(appsResult.error);
|
|
2637
|
+
|
|
2638
|
+
const apps = appsResult.value;
|
|
2639
|
+
const failed: string[] = [];
|
|
2640
|
+
let succeeded = 0;
|
|
2641
|
+
|
|
2642
|
+
for (const app of apps) {
|
|
2643
|
+
const result = await this.restartApplication(app.uuid);
|
|
2644
|
+
if (isErr(result)) {
|
|
2645
|
+
failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
|
|
2646
|
+
} else {
|
|
2647
|
+
succeeded++;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
log.success(`Restarted ${succeeded}/${apps.length} apps in project`);
|
|
2652
|
+
return ok({ total: apps.length, succeeded, failed });
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
/**
|
|
2656
|
+
* Redeploys all applications in a project.
|
|
2657
|
+
*
|
|
2658
|
+
* @param projectUuid - Project UUID
|
|
2659
|
+
* @param force - Force rebuild
|
|
2660
|
+
* @returns Result with batch operation results
|
|
2661
|
+
*/
|
|
2662
|
+
async redeployProjectApps(
|
|
2663
|
+
projectUuid: string,
|
|
2664
|
+
force: boolean = false,
|
|
2665
|
+
): Promise<
|
|
2666
|
+
Result<{ total: number; succeeded: number; failed: string[] }, Error>
|
|
2667
|
+
> {
|
|
2668
|
+
log.info(`Redeploying all apps in project ${projectUuid}`);
|
|
2669
|
+
|
|
2670
|
+
const appsResult = await this.listApplications(undefined, projectUuid);
|
|
2671
|
+
if (isErr(appsResult)) return err(appsResult.error);
|
|
2672
|
+
|
|
2673
|
+
const apps = appsResult.value;
|
|
2674
|
+
const failed: string[] = [];
|
|
2675
|
+
let succeeded = 0;
|
|
2676
|
+
|
|
2677
|
+
for (const app of apps) {
|
|
2678
|
+
const result = await this.deploy({ uuid: app.uuid, force });
|
|
2679
|
+
if (isErr(result)) {
|
|
2680
|
+
failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
|
|
2681
|
+
} else {
|
|
2682
|
+
succeeded++;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
log.success(`Redeployed ${succeeded}/${apps.length} apps in project`);
|
|
2687
|
+
return ok({ total: apps.length, succeeded, failed });
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
/**
|
|
2691
|
+
* Stops all running applications.
|
|
2692
|
+
*
|
|
2693
|
+
* @returns Result with batch operation results
|
|
2694
|
+
*/
|
|
2695
|
+
async stopAllApps(): Promise<
|
|
2696
|
+
Result<{ total: number; succeeded: number; failed: string[] }, Error>
|
|
2697
|
+
> {
|
|
2698
|
+
log.info("Stopping all applications");
|
|
2699
|
+
|
|
2700
|
+
const appsResult = await this.listApplications();
|
|
2701
|
+
if (isErr(appsResult)) return err(appsResult.error);
|
|
2702
|
+
|
|
2703
|
+
const running = appsResult.value.filter(
|
|
2704
|
+
(a) => !a.status?.includes("stopped"),
|
|
2705
|
+
);
|
|
2706
|
+
const failed: string[] = [];
|
|
2707
|
+
let succeeded = 0;
|
|
2708
|
+
|
|
2709
|
+
for (const app of running) {
|
|
2710
|
+
const result = await this.stopApplication(app.uuid);
|
|
2711
|
+
if (isErr(result)) {
|
|
2712
|
+
failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
|
|
2713
|
+
} else {
|
|
2714
|
+
succeeded++;
|
|
2715
|
+
}
|
|
992
2716
|
}
|
|
993
2717
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
2718
|
+
log.success(`Stopped ${succeeded}/${running.length} apps`);
|
|
2719
|
+
return ok({ total: running.length, succeeded, failed });
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// ===========================================================================
|
|
2723
|
+
// Summary Types (Token Optimization for MCP)
|
|
2724
|
+
// ===========================================================================
|
|
2725
|
+
|
|
2726
|
+
/**
|
|
2727
|
+
* Lists applications with minimal fields for token efficiency.
|
|
2728
|
+
*
|
|
2729
|
+
* @returns Result with application summaries or error
|
|
2730
|
+
*/
|
|
2731
|
+
async listApplicationSummaries(): Promise<
|
|
2732
|
+
Result<
|
|
2733
|
+
Array<{
|
|
2734
|
+
uuid: string;
|
|
2735
|
+
name: string;
|
|
2736
|
+
status: string;
|
|
2737
|
+
fqdn: string | null;
|
|
2738
|
+
}>,
|
|
2739
|
+
Error
|
|
2740
|
+
>
|
|
2741
|
+
> {
|
|
2742
|
+
const result = await this.listApplications();
|
|
2743
|
+
if (isErr(result)) return err(result.error);
|
|
2744
|
+
|
|
2745
|
+
return ok(
|
|
2746
|
+
result.value.map((a) => ({
|
|
2747
|
+
uuid: a.uuid,
|
|
2748
|
+
name: a.name,
|
|
2749
|
+
status: a.status,
|
|
2750
|
+
fqdn: a.fqdn || null,
|
|
2751
|
+
})),
|
|
2752
|
+
);
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* Lists servers with minimal fields for token efficiency.
|
|
2757
|
+
*
|
|
2758
|
+
* @returns Result with server summaries or error
|
|
2759
|
+
*/
|
|
2760
|
+
async listServerSummaries(): Promise<
|
|
2761
|
+
Result<
|
|
2762
|
+
Array<{
|
|
2763
|
+
uuid: string;
|
|
2764
|
+
name: string;
|
|
2765
|
+
ip: string;
|
|
2766
|
+
is_reachable: boolean;
|
|
2767
|
+
}>,
|
|
2768
|
+
Error
|
|
2769
|
+
>
|
|
2770
|
+
> {
|
|
2771
|
+
const result = await this.listServers();
|
|
2772
|
+
if (isErr(result)) return err(result.error);
|
|
2773
|
+
|
|
2774
|
+
return ok(
|
|
2775
|
+
result.value.map((s) => ({
|
|
2776
|
+
uuid: s.uuid,
|
|
2777
|
+
name: s.name,
|
|
2778
|
+
ip: s.ip || "",
|
|
2779
|
+
is_reachable: s.is_reachable || false,
|
|
2780
|
+
})),
|
|
2781
|
+
);
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
/**
|
|
2785
|
+
* Lists databases with minimal fields for token efficiency.
|
|
2786
|
+
*
|
|
2787
|
+
* @returns Result with database summaries or error
|
|
2788
|
+
*/
|
|
2789
|
+
async listDatabaseSummaries(): Promise<
|
|
2790
|
+
Result<
|
|
2791
|
+
Array<{
|
|
2792
|
+
uuid: string;
|
|
2793
|
+
name: string;
|
|
2794
|
+
type: string;
|
|
2795
|
+
status: string;
|
|
2796
|
+
}>,
|
|
2797
|
+
Error
|
|
2798
|
+
>
|
|
2799
|
+
> {
|
|
2800
|
+
const result = await this.listDatabases();
|
|
2801
|
+
if (isErr(result)) return err(result.error);
|
|
2802
|
+
|
|
2803
|
+
return ok(
|
|
2804
|
+
result.value.map((d) => ({
|
|
2805
|
+
uuid: d.uuid,
|
|
2806
|
+
name: d.name,
|
|
2807
|
+
type: d.type,
|
|
2808
|
+
status: d.status,
|
|
2809
|
+
})),
|
|
2810
|
+
);
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
/**
|
|
2814
|
+
* Lists services with minimal fields for token efficiency.
|
|
2815
|
+
*
|
|
2816
|
+
* @returns Result with service summaries or error
|
|
2817
|
+
*/
|
|
2818
|
+
async listServiceSummaries(): Promise<
|
|
2819
|
+
Result<
|
|
2820
|
+
Array<{
|
|
2821
|
+
uuid: string;
|
|
2822
|
+
name: string;
|
|
2823
|
+
type: string;
|
|
2824
|
+
status: string;
|
|
2825
|
+
}>,
|
|
2826
|
+
Error
|
|
2827
|
+
>
|
|
2828
|
+
> {
|
|
2829
|
+
const result = await this.listServices();
|
|
2830
|
+
if (isErr(result)) return err(result.error);
|
|
2831
|
+
|
|
2832
|
+
return ok(
|
|
2833
|
+
result.value.map((s) => ({
|
|
2834
|
+
uuid: s.uuid,
|
|
2835
|
+
name: s.name,
|
|
2836
|
+
type: s.type,
|
|
2837
|
+
status: s.status,
|
|
2838
|
+
})),
|
|
2839
|
+
);
|
|
997
2840
|
}
|
|
998
2841
|
}
|
|
999
2842
|
|
|
1000
|
-
let instance: CoolifyService | null = null
|
|
2843
|
+
let instance: CoolifyService | null = null;
|
|
1001
2844
|
|
|
1002
2845
|
/**
|
|
1003
2846
|
* Gets the singleton CoolifyService instance.
|
|
@@ -1006,19 +2849,26 @@ let instance: CoolifyService | null = null
|
|
|
1006
2849
|
*/
|
|
1007
2850
|
export function getCoolifyService(): CoolifyService {
|
|
1008
2851
|
if (!instance) {
|
|
1009
|
-
instance = new CoolifyService()
|
|
2852
|
+
instance = new CoolifyService();
|
|
1010
2853
|
}
|
|
1011
|
-
return instance
|
|
2854
|
+
return instance;
|
|
1012
2855
|
}
|
|
1013
2856
|
|
|
1014
2857
|
// Re-export types
|
|
1015
2858
|
export type {
|
|
1016
2859
|
ICoolifyServer,
|
|
2860
|
+
ICoolifyServerResource,
|
|
2861
|
+
ICoolifyServerDomain,
|
|
1017
2862
|
ICoolifyDestination,
|
|
1018
2863
|
ICoolifyProject,
|
|
1019
2864
|
ICoolifyTeam,
|
|
1020
2865
|
ICoolifyApplication,
|
|
2866
|
+
ICoolifyDatabase,
|
|
2867
|
+
ICoolifyDatabaseBackup,
|
|
2868
|
+
ICoolifyService as ICoolifyServiceType,
|
|
2869
|
+
ICoolifyPrivateKey,
|
|
1021
2870
|
ICoolifyDeployment,
|
|
2871
|
+
ICoolifyVersion,
|
|
1022
2872
|
ICoolifyAppOptions,
|
|
1023
2873
|
ICoolifyAppResult,
|
|
1024
2874
|
ICoolifyDeployOptions,
|
|
@@ -1028,4 +2878,4 @@ export type {
|
|
|
1028
2878
|
ICoolifyLogsOptions,
|
|
1029
2879
|
ICoolifyLogs,
|
|
1030
2880
|
IProgressCallback,
|
|
1031
|
-
} from
|
|
2881
|
+
} from "./types.js";
|