@masonator/coolify-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +375 -0
- package/dist/__tests__/coolify-client.test.d.ts +1 -0
- package/dist/__tests__/coolify-client.test.js +325 -0
- package/dist/__tests__/mcp-server.test.d.ts +1 -0
- package/dist/__tests__/mcp-server.test.js +209 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/dist/lib/coolify-client.d.ts +32 -0
- package/dist/lib/coolify-client.js +113 -0
- package/dist/lib/mcp-server.d.ts +42 -0
- package/dist/lib/mcp-server.js +362 -0
- package/dist/types/coolify.d.ts +69 -0
- package/dist/types/coolify.js +2 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Coolify MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server implementation for [Coolify](https://coolify.io/), enabling AI assistants to interact with your Coolify instances through natural language.
|
|
4
|
+
|
|
5
|
+
## Claude Desktop, Cursor, and other MCP compatible IDEs
|
|
6
|
+
|
|
7
|
+
### Claude
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
"coolify": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": [
|
|
13
|
+
"-y", "@stumason/coolify-mcp"
|
|
14
|
+
],
|
|
15
|
+
"env": {
|
|
16
|
+
"COOLIFY_ACCESS_TOKEN": "0|your-secret-token",
|
|
17
|
+
"COOLIFY_BASE_URL": "https://your-coolify-instance.com"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Cursor
|
|
23
|
+
|
|
24
|
+
command:
|
|
25
|
+
|
|
26
|
+
`env COOLIFY_ACCESS_TOKEN:0|your-secret-token COOLIFY_BASE_URL:https://your-coolify-instance.com npx -y @stumason/coolify-mcp`
|
|
27
|
+
|
|
28
|
+
## Overview
|
|
29
|
+
|
|
30
|
+
This MCP server provides a standardized interface for AI models to:
|
|
31
|
+
|
|
32
|
+
- Query Coolify server information
|
|
33
|
+
- Manage projects and environments
|
|
34
|
+
- Handle deployments and resources
|
|
35
|
+
- Monitor server status
|
|
36
|
+
- And more...
|
|
37
|
+
|
|
38
|
+
By implementing the [Model Context Protocol](https://modelcontextprotocol.io), this server allows AI assistants to understand and interact with your Coolify infrastructure in a secure and controlled manner.
|
|
39
|
+
|
|
40
|
+
## Prerequisites
|
|
41
|
+
|
|
42
|
+
- Node.js >= 18
|
|
43
|
+
- A running Coolify instance
|
|
44
|
+
- Coolify API access token
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Clone the repository
|
|
50
|
+
git clone https://github.com/stumason/coolify-mcp.git
|
|
51
|
+
cd coolify-mcp
|
|
52
|
+
|
|
53
|
+
# Install dependencies
|
|
54
|
+
npm install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
The server requires the following environment variables:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Required
|
|
63
|
+
COOLIFY_ACCESS_TOKEN=your_access_token_here
|
|
64
|
+
|
|
65
|
+
# Optional (defaults to http://localhost:3000)
|
|
66
|
+
COOLIFY_BASE_URL=https://your.coolify.instance
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Available Tools
|
|
70
|
+
|
|
71
|
+
### Server Management
|
|
72
|
+
|
|
73
|
+
#### list_servers
|
|
74
|
+
|
|
75
|
+
Lists all Coolify servers in your instance.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Returns: ServerInfo[]
|
|
79
|
+
await client.list_servers();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### get_server
|
|
83
|
+
|
|
84
|
+
Get detailed information about a specific server.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Returns: ServerInfo
|
|
88
|
+
await client.get_server(uuid: string)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### get_server_resources
|
|
92
|
+
|
|
93
|
+
Get current resources (applications, databases, etc.) running on a server.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Returns: ServerResources[]
|
|
97
|
+
await client.get_server_resources(uuid: string)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### get_server_domains
|
|
101
|
+
|
|
102
|
+
Get domains associated with a server.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Returns: ServerDomain[]
|
|
106
|
+
await client.get_server_domains(uuid: string)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### validate_server
|
|
110
|
+
|
|
111
|
+
Validate the connection to a specific server.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Returns: ValidationResponse
|
|
115
|
+
await client.validate_server(uuid: string)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Project Management
|
|
119
|
+
|
|
120
|
+
#### list_projects
|
|
121
|
+
|
|
122
|
+
List all projects in your Coolify instance.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Returns: Project[]
|
|
126
|
+
await client.list_projects();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### get_project
|
|
130
|
+
|
|
131
|
+
Get detailed information about a specific project.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Returns: Project
|
|
135
|
+
await client.get_project(uuid: string)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### create_project
|
|
139
|
+
|
|
140
|
+
Create a new project.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Returns: { uuid: string }
|
|
144
|
+
await client.create_project({
|
|
145
|
+
name: string,
|
|
146
|
+
description?: string
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### update_project
|
|
151
|
+
|
|
152
|
+
Update an existing project's details.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// Returns: Project
|
|
156
|
+
await client.update_project(uuid: string, {
|
|
157
|
+
name?: string,
|
|
158
|
+
description?: string
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### delete_project
|
|
163
|
+
|
|
164
|
+
Delete a project.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// Returns: { message: string }
|
|
168
|
+
await client.delete_project(uuid: string)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Environment Management
|
|
172
|
+
|
|
173
|
+
#### list_environments
|
|
174
|
+
|
|
175
|
+
List all environments or environments for a specific project.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Returns: Environment[]
|
|
179
|
+
await client.list_environments(project_uuid?: string)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### get_environment
|
|
183
|
+
|
|
184
|
+
Get detailed information about a specific environment.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Returns: Environment
|
|
188
|
+
await client.get_environment(uuid: string)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### get_project_environment
|
|
192
|
+
|
|
193
|
+
Get a specific environment within a project.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Returns: Environment
|
|
197
|
+
await client.get_project_environment(
|
|
198
|
+
project_uuid: string,
|
|
199
|
+
environment_name_or_uuid: string
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### create_environment
|
|
204
|
+
|
|
205
|
+
Create a new environment.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Returns: { uuid: string }
|
|
209
|
+
await client.create_environment({
|
|
210
|
+
name: string,
|
|
211
|
+
project_uuid: string,
|
|
212
|
+
variables?: Record<string, string>
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### update_environment_variables
|
|
217
|
+
|
|
218
|
+
Update variables for a specific environment.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Returns: Environment
|
|
222
|
+
await client.update_environment_variables(
|
|
223
|
+
uuid: string,
|
|
224
|
+
{ variables: Record<string, string> }
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### delete_environment
|
|
229
|
+
|
|
230
|
+
Delete an environment.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
// Returns: { message: string }
|
|
234
|
+
await client.delete_environment(uuid: string)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Type Definitions
|
|
238
|
+
|
|
239
|
+
### ServerInfo
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
interface ServerInfo {
|
|
243
|
+
uuid: string;
|
|
244
|
+
name: string;
|
|
245
|
+
status: 'running' | 'stopped' | 'error';
|
|
246
|
+
version: string;
|
|
247
|
+
resources: {
|
|
248
|
+
cpu: number;
|
|
249
|
+
memory: number;
|
|
250
|
+
disk: number;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Environment
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
interface Environment {
|
|
259
|
+
id: number;
|
|
260
|
+
uuid: string;
|
|
261
|
+
name: string;
|
|
262
|
+
project_uuid: string;
|
|
263
|
+
variables?: Record<string, string>;
|
|
264
|
+
created_at: string;
|
|
265
|
+
updated_at: string;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Project
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
interface Project {
|
|
273
|
+
id: number;
|
|
274
|
+
uuid: string;
|
|
275
|
+
name: string;
|
|
276
|
+
description?: string;
|
|
277
|
+
environments?: Environment[];
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Development
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Run in development mode
|
|
285
|
+
npm run build -- --watch
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Testing
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Run all tests
|
|
292
|
+
npm test
|
|
293
|
+
|
|
294
|
+
# Run tests in watch mode
|
|
295
|
+
npm test -- --watch
|
|
296
|
+
|
|
297
|
+
# Run tests with coverage
|
|
298
|
+
npm test -- --coverage
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Code Quality
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Run linter
|
|
305
|
+
npm run lint
|
|
306
|
+
|
|
307
|
+
# Fix linting issues
|
|
308
|
+
npm run lint -- --fix
|
|
309
|
+
|
|
310
|
+
# Format code
|
|
311
|
+
npm run format
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Architecture
|
|
315
|
+
|
|
316
|
+
The project follows a modular architecture:
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
src/
|
|
320
|
+
├── lib/
|
|
321
|
+
│ ├── coolify-client.ts # Coolify API client
|
|
322
|
+
│ └── mcp-server.ts # MCP server implementation
|
|
323
|
+
├── types/
|
|
324
|
+
│ └── coolify.ts # TypeScript type definitions
|
|
325
|
+
└── index.ts # Entry point
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Key Components
|
|
329
|
+
|
|
330
|
+
1. **CoolifyClient**: Handles communication with the Coolify API
|
|
331
|
+
|
|
332
|
+
- Authentication
|
|
333
|
+
- API request handling
|
|
334
|
+
- Error management
|
|
335
|
+
|
|
336
|
+
2. **CoolifyMcpServer**: Implements the MCP server
|
|
337
|
+
- Resource management
|
|
338
|
+
- Tool implementations
|
|
339
|
+
- Client request handling
|
|
340
|
+
|
|
341
|
+
## Contributing
|
|
342
|
+
|
|
343
|
+
1. Fork the repository
|
|
344
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
345
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
346
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
347
|
+
5. Open a Pull Request
|
|
348
|
+
|
|
349
|
+
### Development Guidelines
|
|
350
|
+
|
|
351
|
+
- Follow TypeScript best practices
|
|
352
|
+
- Maintain test coverage
|
|
353
|
+
- Update documentation as needed
|
|
354
|
+
- Follow the established code style
|
|
355
|
+
|
|
356
|
+
## CI/CD
|
|
357
|
+
|
|
358
|
+
GitHub Actions workflows are configured to:
|
|
359
|
+
|
|
360
|
+
- Run tests on Node.js 18.x and 20.x
|
|
361
|
+
- Check code formatting
|
|
362
|
+
- Verify build process
|
|
363
|
+
- Run linting checks
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
MIT
|
|
368
|
+
|
|
369
|
+
## Support
|
|
370
|
+
|
|
371
|
+
For support, please:
|
|
372
|
+
|
|
373
|
+
1. Check the [issues](https://github.com/stumason/coolify-mcp/issues) page
|
|
374
|
+
2. Create a new issue if needed
|
|
375
|
+
3. Join the Coolify community
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const coolify_client_js_1 = require("../lib/coolify-client.js");
|
|
4
|
+
// Mock fetch globally
|
|
5
|
+
const mockFetch = jest.fn();
|
|
6
|
+
global.fetch = mockFetch;
|
|
7
|
+
describe('CoolifyClient', () => {
|
|
8
|
+
let client;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
client = new coolify_client_js_1.CoolifyClient({
|
|
11
|
+
baseUrl: 'http://test.coolify.io',
|
|
12
|
+
accessToken: 'test-token',
|
|
13
|
+
});
|
|
14
|
+
mockFetch.mockClear();
|
|
15
|
+
// Reset any global mock implementations
|
|
16
|
+
mockFetch.mockReset();
|
|
17
|
+
});
|
|
18
|
+
describe('listServers', () => {
|
|
19
|
+
const mockServers = [
|
|
20
|
+
{
|
|
21
|
+
uuid: 'test-id-1',
|
|
22
|
+
name: 'test-server-1',
|
|
23
|
+
status: 'running',
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
resources: {
|
|
26
|
+
cpu: 50,
|
|
27
|
+
memory: 60,
|
|
28
|
+
disk: 70,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
uuid: 'test-id-2',
|
|
33
|
+
name: 'test-server-2',
|
|
34
|
+
status: 'stopped',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
resources: {
|
|
37
|
+
cpu: 0,
|
|
38
|
+
memory: 0,
|
|
39
|
+
disk: 70,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
it('should fetch server list successfully', async () => {
|
|
44
|
+
mockFetch.mockResolvedValueOnce({
|
|
45
|
+
ok: true,
|
|
46
|
+
json: async () => mockServers,
|
|
47
|
+
});
|
|
48
|
+
const result = await client.listServers();
|
|
49
|
+
expect(result).toEqual(mockServers);
|
|
50
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/servers', expect.objectContaining({
|
|
51
|
+
headers: expect.objectContaining({
|
|
52
|
+
Authorization: 'Bearer test-token',
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
}),
|
|
55
|
+
}));
|
|
56
|
+
});
|
|
57
|
+
it('should handle error responses', async () => {
|
|
58
|
+
const errorResponse = {
|
|
59
|
+
error: 'Unauthorized',
|
|
60
|
+
status: 401,
|
|
61
|
+
message: 'Invalid token',
|
|
62
|
+
};
|
|
63
|
+
mockFetch.mockResolvedValueOnce({
|
|
64
|
+
ok: false,
|
|
65
|
+
json: async () => errorResponse,
|
|
66
|
+
});
|
|
67
|
+
await expect(client.listServers()).rejects.toThrow('Invalid token');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('getServer', () => {
|
|
71
|
+
const mockServerInfo = {
|
|
72
|
+
uuid: 'test-id',
|
|
73
|
+
name: 'test-server',
|
|
74
|
+
status: 'running',
|
|
75
|
+
version: '1.0.0',
|
|
76
|
+
resources: {
|
|
77
|
+
cpu: 50,
|
|
78
|
+
memory: 60,
|
|
79
|
+
disk: 70,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
it('should fetch server info successfully', async () => {
|
|
83
|
+
mockFetch.mockResolvedValueOnce({
|
|
84
|
+
ok: true,
|
|
85
|
+
json: async () => mockServerInfo,
|
|
86
|
+
});
|
|
87
|
+
const result = await client.getServer('test-id');
|
|
88
|
+
expect(result).toEqual(mockServerInfo);
|
|
89
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/servers/test-id', expect.objectContaining({
|
|
90
|
+
headers: expect.objectContaining({
|
|
91
|
+
Authorization: 'Bearer test-token',
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
}),
|
|
94
|
+
}));
|
|
95
|
+
});
|
|
96
|
+
it('should handle error responses', async () => {
|
|
97
|
+
const errorResponse = {
|
|
98
|
+
error: 'Not Found',
|
|
99
|
+
status: 404,
|
|
100
|
+
message: 'Server not found',
|
|
101
|
+
};
|
|
102
|
+
mockFetch.mockResolvedValueOnce({
|
|
103
|
+
ok: false,
|
|
104
|
+
json: async () => errorResponse,
|
|
105
|
+
});
|
|
106
|
+
await expect(client.getServer('invalid-id')).rejects.toThrow('Server not found');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('getServerResources', () => {
|
|
110
|
+
const mockServerResources = [
|
|
111
|
+
{
|
|
112
|
+
id: 1,
|
|
113
|
+
uuid: 'test-id',
|
|
114
|
+
name: 'test-app',
|
|
115
|
+
type: 'application',
|
|
116
|
+
created_at: '2024-03-19T12:00:00Z',
|
|
117
|
+
updated_at: '2024-03-19T12:00:00Z',
|
|
118
|
+
status: 'running:healthy',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
it('should fetch server resources successfully', async () => {
|
|
122
|
+
mockFetch.mockResolvedValueOnce({
|
|
123
|
+
ok: true,
|
|
124
|
+
json: async () => mockServerResources,
|
|
125
|
+
});
|
|
126
|
+
const result = await client.getServerResources('test-id');
|
|
127
|
+
expect(result).toEqual(mockServerResources);
|
|
128
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/servers/test-id/resources', expect.objectContaining({
|
|
129
|
+
headers: expect.objectContaining({
|
|
130
|
+
Authorization: 'Bearer test-token',
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
}),
|
|
133
|
+
}));
|
|
134
|
+
});
|
|
135
|
+
it('should handle error responses', async () => {
|
|
136
|
+
const errorResponse = {
|
|
137
|
+
error: 'Server Error',
|
|
138
|
+
status: 500,
|
|
139
|
+
message: 'Internal server error',
|
|
140
|
+
};
|
|
141
|
+
mockFetch.mockResolvedValueOnce({
|
|
142
|
+
ok: false,
|
|
143
|
+
json: async () => errorResponse,
|
|
144
|
+
});
|
|
145
|
+
await expect(client.getServerResources('test-id')).rejects.toThrow('Internal server error');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
test('getServer returns server info', async () => {
|
|
149
|
+
const mockResponse = {
|
|
150
|
+
uuid: 'test-uuid',
|
|
151
|
+
name: 'test-server',
|
|
152
|
+
status: 'running',
|
|
153
|
+
version: '1.0.0',
|
|
154
|
+
resources: {
|
|
155
|
+
cpu: 50,
|
|
156
|
+
memory: 60,
|
|
157
|
+
disk: 70,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
mockFetch.mockResolvedValueOnce({
|
|
161
|
+
ok: true,
|
|
162
|
+
json: () => Promise.resolve(mockResponse),
|
|
163
|
+
});
|
|
164
|
+
const server = await client.getServer('test-uuid');
|
|
165
|
+
expect(server).toEqual(mockResponse);
|
|
166
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/servers/test-uuid', expect.objectContaining({
|
|
167
|
+
headers: {
|
|
168
|
+
Authorization: 'Bearer test-token',
|
|
169
|
+
'Content-Type': 'application/json',
|
|
170
|
+
},
|
|
171
|
+
}));
|
|
172
|
+
});
|
|
173
|
+
test('getServerResources returns server resources', async () => {
|
|
174
|
+
const mockResponse = [
|
|
175
|
+
{
|
|
176
|
+
id: 1,
|
|
177
|
+
uuid: 'test-uuid',
|
|
178
|
+
name: 'test-app',
|
|
179
|
+
type: 'application',
|
|
180
|
+
created_at: '2025-03-05T13:41:12.000Z',
|
|
181
|
+
updated_at: '2025-03-05T13:41:12.000Z',
|
|
182
|
+
status: 'running:healthy',
|
|
183
|
+
},
|
|
184
|
+
];
|
|
185
|
+
mockFetch.mockResolvedValueOnce({
|
|
186
|
+
ok: true,
|
|
187
|
+
json: () => Promise.resolve(mockResponse),
|
|
188
|
+
});
|
|
189
|
+
const resources = await client.getServerResources('test-uuid');
|
|
190
|
+
expect(resources).toEqual(mockResponse);
|
|
191
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/servers/test-uuid/resources', expect.objectContaining({
|
|
192
|
+
headers: {
|
|
193
|
+
Authorization: 'Bearer test-token',
|
|
194
|
+
'Content-Type': 'application/json',
|
|
195
|
+
},
|
|
196
|
+
}));
|
|
197
|
+
});
|
|
198
|
+
describe('Environment Management', () => {
|
|
199
|
+
const mockEnvironment = {
|
|
200
|
+
id: 1,
|
|
201
|
+
uuid: 'env-test-id',
|
|
202
|
+
name: 'test-env',
|
|
203
|
+
project_uuid: 'project-test-id',
|
|
204
|
+
variables: { KEY: 'value' },
|
|
205
|
+
created_at: '2024-03-19T12:00:00Z',
|
|
206
|
+
updated_at: '2024-03-19T12:00:00Z',
|
|
207
|
+
};
|
|
208
|
+
beforeEach(() => {
|
|
209
|
+
mockFetch.mockClear();
|
|
210
|
+
});
|
|
211
|
+
describe('listEnvironments', () => {
|
|
212
|
+
it('should fetch all environments successfully', async () => {
|
|
213
|
+
mockFetch.mockResolvedValueOnce({
|
|
214
|
+
ok: true,
|
|
215
|
+
json: () => Promise.resolve([mockEnvironment]),
|
|
216
|
+
});
|
|
217
|
+
const result = await client.listEnvironments();
|
|
218
|
+
expect(result).toEqual([mockEnvironment]);
|
|
219
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments', expect.objectContaining({
|
|
220
|
+
headers: expect.objectContaining({
|
|
221
|
+
Authorization: 'Bearer test-token',
|
|
222
|
+
'Content-Type': 'application/json',
|
|
223
|
+
}),
|
|
224
|
+
}));
|
|
225
|
+
});
|
|
226
|
+
it('should fetch environments by project UUID', async () => {
|
|
227
|
+
mockFetch.mockResolvedValueOnce({
|
|
228
|
+
ok: true,
|
|
229
|
+
json: () => Promise.resolve([mockEnvironment]),
|
|
230
|
+
});
|
|
231
|
+
const result = await client.listEnvironments('project-test-id');
|
|
232
|
+
expect(result).toEqual([mockEnvironment]);
|
|
233
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments?project_uuid=project-test-id', expect.objectContaining({
|
|
234
|
+
headers: expect.objectContaining({
|
|
235
|
+
Authorization: 'Bearer test-token',
|
|
236
|
+
'Content-Type': 'application/json',
|
|
237
|
+
}),
|
|
238
|
+
}));
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('getEnvironment', () => {
|
|
242
|
+
it('should fetch environment by UUID successfully', async () => {
|
|
243
|
+
mockFetch.mockResolvedValueOnce({
|
|
244
|
+
ok: true,
|
|
245
|
+
json: () => Promise.resolve(mockEnvironment),
|
|
246
|
+
});
|
|
247
|
+
const result = await client.getEnvironment('env-test-id');
|
|
248
|
+
expect(result).toEqual(mockEnvironment);
|
|
249
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments/env-test-id', expect.objectContaining({
|
|
250
|
+
headers: expect.objectContaining({
|
|
251
|
+
Authorization: 'Bearer test-token',
|
|
252
|
+
'Content-Type': 'application/json',
|
|
253
|
+
}),
|
|
254
|
+
}));
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
describe('createEnvironment', () => {
|
|
258
|
+
it('should create environment successfully', async () => {
|
|
259
|
+
const createRequest = {
|
|
260
|
+
name: 'test-env',
|
|
261
|
+
project_uuid: 'project-test-id',
|
|
262
|
+
variables: { KEY: 'value' },
|
|
263
|
+
};
|
|
264
|
+
const mockResponse = { uuid: 'env-test-id' };
|
|
265
|
+
mockFetch.mockResolvedValueOnce({
|
|
266
|
+
ok: true,
|
|
267
|
+
json: () => Promise.resolve(mockResponse),
|
|
268
|
+
});
|
|
269
|
+
const result = await client.createEnvironment(createRequest);
|
|
270
|
+
expect(result).toEqual(mockResponse);
|
|
271
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments', expect.objectContaining({
|
|
272
|
+
method: 'POST',
|
|
273
|
+
body: JSON.stringify(createRequest),
|
|
274
|
+
headers: expect.objectContaining({
|
|
275
|
+
Authorization: 'Bearer test-token',
|
|
276
|
+
'Content-Type': 'application/json',
|
|
277
|
+
}),
|
|
278
|
+
}));
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe('updateEnvironmentVariables', () => {
|
|
282
|
+
it('should update environment variables successfully', async () => {
|
|
283
|
+
const updateRequest = {
|
|
284
|
+
variables: { NEW_KEY: 'new-value' },
|
|
285
|
+
};
|
|
286
|
+
const mockUpdatedEnvironment = {
|
|
287
|
+
...mockEnvironment,
|
|
288
|
+
variables: updateRequest.variables,
|
|
289
|
+
};
|
|
290
|
+
mockFetch.mockResolvedValueOnce({
|
|
291
|
+
ok: true,
|
|
292
|
+
json: () => Promise.resolve(mockUpdatedEnvironment),
|
|
293
|
+
});
|
|
294
|
+
const result = await client.updateEnvironmentVariables('env-test-id', updateRequest);
|
|
295
|
+
expect(result).toEqual(mockUpdatedEnvironment);
|
|
296
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments/env-test-id/variables', expect.objectContaining({
|
|
297
|
+
method: 'PUT',
|
|
298
|
+
body: JSON.stringify(updateRequest),
|
|
299
|
+
headers: expect.objectContaining({
|
|
300
|
+
Authorization: 'Bearer test-token',
|
|
301
|
+
'Content-Type': 'application/json',
|
|
302
|
+
}),
|
|
303
|
+
}));
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
describe('deleteEnvironment', () => {
|
|
307
|
+
it('should delete environment successfully', async () => {
|
|
308
|
+
const mockResponse = { message: 'Environment deleted successfully' };
|
|
309
|
+
mockFetch.mockResolvedValueOnce({
|
|
310
|
+
ok: true,
|
|
311
|
+
json: () => Promise.resolve(mockResponse),
|
|
312
|
+
});
|
|
313
|
+
const result = await client.deleteEnvironment('env-test-id');
|
|
314
|
+
expect(result).toEqual(mockResponse);
|
|
315
|
+
expect(mockFetch).toHaveBeenCalledWith('http://test.coolify.io/api/v1/environments/env-test-id', expect.objectContaining({
|
|
316
|
+
method: 'DELETE',
|
|
317
|
+
headers: expect.objectContaining({
|
|
318
|
+
Authorization: 'Bearer test-token',
|
|
319
|
+
'Content-Type': 'application/json',
|
|
320
|
+
}),
|
|
321
|
+
}));
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|