@ninedeploy/schemas 0.10.6 → 0.16.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/LICENSE +21 -21
- package/dist/accessGrants.d.ts +156 -0
- package/dist/accessGrants.d.ts.map +1 -0
- package/dist/accessGrants.js +100 -0
- package/dist/accessGrants.js.map +1 -0
- package/dist/agentCommand.d.ts.map +1 -1
- package/dist/agentCommand.js +11 -1
- package/dist/agentCommand.js.map +1 -1
- package/dist/auth.d.ts +53 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +26 -0
- package/dist/auth.js.map +1 -1
- package/dist/backupPolicy.d.ts +37 -0
- package/dist/backupPolicy.d.ts.map +1 -0
- package/dist/backupPolicy.js +50 -0
- package/dist/backupPolicy.js.map +1 -0
- package/dist/common.d.ts +21 -5
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +35 -6
- package/dist/common.js.map +1 -1
- package/dist/databaseImport.d.ts +145 -0
- package/dist/databaseImport.d.ts.map +1 -0
- package/dist/databaseImport.js +115 -0
- package/dist/databaseImport.js.map +1 -0
- package/dist/doctor.d.ts +16 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +25 -0
- package/dist/doctor.js.map +1 -1
- package/dist/githubApp.d.ts +236 -0
- package/dist/githubApp.d.ts.map +1 -0
- package/dist/githubApp.js +223 -0
- package/dist/githubApp.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts +5 -2
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +11 -2
- package/dist/kernel.js.map +1 -1
- package/dist/management.d.ts +46 -2
- package/dist/management.d.ts.map +1 -1
- package/dist/management.js +68 -15
- package/dist/management.js.map +1 -1
- package/dist/multiNode.d.ts +325 -0
- package/dist/multiNode.d.ts.map +1 -0
- package/dist/multiNode.js +294 -0
- package/dist/multiNode.js.map +1 -0
- package/dist/ninedeployManifest.d.ts.map +1 -1
- package/dist/ninedeployManifest.js +6 -3
- package/dist/ninedeployManifest.js.map +1 -1
- package/dist/panelBackup.d.ts +73 -0
- package/dist/panelBackup.d.ts.map +1 -0
- package/dist/panelBackup.js +45 -0
- package/dist/panelBackup.js.map +1 -0
- package/dist/proxyConfig.d.ts +132 -0
- package/dist/proxyConfig.d.ts.map +1 -0
- package/dist/proxyConfig.js +105 -0
- package/dist/proxyConfig.js.map +1 -0
- package/dist/publicAccess.d.ts +64 -0
- package/dist/publicAccess.d.ts.map +1 -0
- package/dist/publicAccess.js +63 -0
- package/dist/publicAccess.js.map +1 -0
- package/dist/secretProviders.d.ts +119 -0
- package/dist/secretProviders.d.ts.map +1 -0
- package/dist/secretProviders.js +130 -0
- package/dist/secretProviders.js.map +1 -0
- package/dist/secretScan.js +1 -1
- package/dist/service.d.ts +24 -17
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +71 -26
- package/dist/service.js.map +1 -1
- package/dist/terminals.d.ts +284 -0
- package/dist/terminals.d.ts.map +1 -0
- package/dist/terminals.js +177 -0
- package/dist/terminals.js.map +1 -0
- package/dist/traffic.d.ts +276 -0
- package/dist/traffic.d.ts.map +1 -0
- package/dist/traffic.js +103 -0
- package/dist/traffic.js.map +1 -0
- package/dist/workspaces.d.ts +19 -1
- package/dist/workspaces.d.ts.map +1 -1
- package/dist/workspaces.js +26 -0
- package/dist/workspaces.js.map +1 -1
- package/package.json +5 -4
- package/src/accessGrants.ts +117 -0
- package/src/agentCommand.ts +67 -0
- package/src/ai.ts +32 -0
- package/src/auth.ts +323 -0
- package/src/backupPolicy.ts +55 -0
- package/src/common.ts +179 -0
- package/src/databaseImport.ts +138 -0
- package/src/doctor.ts +161 -0
- package/src/environments.ts +23 -0
- package/src/githubApp.ts +268 -0
- package/src/index.ts +30 -0
- package/src/insights.ts +78 -0
- package/src/kernel.ts +180 -0
- package/src/labels.ts +86 -0
- package/src/management.ts +620 -0
- package/src/multiNode.ts +354 -0
- package/src/ninedeployManifest.ts +363 -0
- package/src/panelBackup.ts +90 -0
- package/src/project.ts +42 -0
- package/src/proxyConfig.ts +139 -0
- package/src/publicAccess.ts +80 -0
- package/src/runtimeVersions.ts +232 -0
- package/src/secretProviders.ts +159 -0
- package/src/secretScan.ts +169 -0
- package/src/service.ts +1044 -0
- package/src/terminals.ts +212 -0
- package/src/traffic.ts +130 -0
- package/src/workspaces.ts +215 -0
package/src/multiNode.ts
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { id } from './common.js';
|
|
3
|
+
import { volumeEntry } from './service.js';
|
|
4
|
+
|
|
5
|
+
// ── Multi-node (the 0.16 theme, shipped in the 0.15.x series) ──────────────
|
|
6
|
+
// Contracts for remote nodes that match the panel host: build placement and
|
|
7
|
+
// image transfer, server roles, node volumes, Swarm, and the agent capability
|
|
8
|
+
// view. These are SHAPE checks; the server authorises every request and
|
|
9
|
+
// re-checks every capability. Design: .temp_files/run_0.16/DESIGN.md §1–§7.
|
|
10
|
+
//
|
|
11
|
+
// Every stored default reproduces 0.15: a NULL `buildOn` builds where the
|
|
12
|
+
// service runs, a NULL `orchestrator` runs plain containers, a server is not
|
|
13
|
+
// a build server, and a database or volume with no `serverId` lives on the
|
|
14
|
+
// panel host.
|
|
15
|
+
|
|
16
|
+
// ── agent capabilities (§1.1) ──────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The capabilities a multi-node agent advertises in its sealed `agent.ping`,
|
|
20
|
+
* appended in this order after the 0.15 list so the 0.15 prefix check holds.
|
|
21
|
+
* The agent's `AGENT_CAPABILITIES` must end with exactly this list.
|
|
22
|
+
*/
|
|
23
|
+
export const MULTI_NODE_CAPABILITIES = [
|
|
24
|
+
'stream',
|
|
25
|
+
'docker.runSpec',
|
|
26
|
+
'volume.manage',
|
|
27
|
+
'image.manage',
|
|
28
|
+
'build.nixpacks',
|
|
29
|
+
'build.railpack',
|
|
30
|
+
'git.sshkey',
|
|
31
|
+
'db.manage',
|
|
32
|
+
'swarm',
|
|
33
|
+
] as const;
|
|
34
|
+
export const multiNodeCapability = z.enum(MULTI_NODE_CAPABILITIES);
|
|
35
|
+
export type MultiNodeCapability = z.infer<typeof multiNodeCapability>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Error codes the multi-node routes answer with. `node_agent_outdated` (422)
|
|
39
|
+
* is the one refusal every feature shares when the node's agent predates it.
|
|
40
|
+
*/
|
|
41
|
+
export const multiNodeErrorCode = z.enum([
|
|
42
|
+
'node_agent_outdated',
|
|
43
|
+
// T2: a current agent whose owner switched the feature off (a node kill switch).
|
|
44
|
+
'node_feature_disabled',
|
|
45
|
+
'node_transport_unsealed',
|
|
46
|
+
'node_unreachable',
|
|
47
|
+
'node_placement_operator_only',
|
|
48
|
+
'node_volume_exists',
|
|
49
|
+
'server_not_online',
|
|
50
|
+
'server_hosts_databases',
|
|
51
|
+
'attachment_host_mismatch',
|
|
52
|
+
'fanout_database_host',
|
|
53
|
+
'remote_database',
|
|
54
|
+
// T5: a volume backup taken on another host (restore needs `?acrossHosts=true`).
|
|
55
|
+
'backup_host_mismatch',
|
|
56
|
+
// T5: the volume file manager works on panel-host volumes only.
|
|
57
|
+
'node_volume_files_unsupported',
|
|
58
|
+
// T7 (review M2): a current agent whose owner has not opted in to Swarm (NINEDEPLOY_AGENT_SWARM_MANAGER unset).
|
|
59
|
+
'node_swarm_not_enabled',
|
|
60
|
+
// ── 0.16 T8 surfaces ── codes the 0.15.4 Swarm routes already answer with.
|
|
61
|
+
// 502: the manager could not confirm the node a join reported (modules/swarm.ts).
|
|
62
|
+
'swarm_node_unverified',
|
|
63
|
+
// 502: the panel host's daemon cannot create an encrypted overlay (init, join).
|
|
64
|
+
'swarm_overlay_unavailable',
|
|
65
|
+
// 409: DELETE /v1/servers/:id of a node still in the swarm (lib/serverDependents.ts).
|
|
66
|
+
'server_swarm_member',
|
|
67
|
+
// Swarm cluster routes (modules/swarm.ts).
|
|
68
|
+
// 409: POST /v1/swarm/init on a daemon already in a swarm.
|
|
69
|
+
'swarm_already_active',
|
|
70
|
+
// 409: enable or join while the panel host is not an active manager (or has no join address).
|
|
71
|
+
'swarm_not_manager',
|
|
72
|
+
// 409: join of a node already linked; leave of a node not in the swarm.
|
|
73
|
+
'swarm_already_joined',
|
|
74
|
+
'swarm_not_joined',
|
|
75
|
+
// 502: a swarm op failed on the node's agent; `docker swarm init` or the drain failed on the panel host.
|
|
76
|
+
'node_swarm_failed',
|
|
77
|
+
'swarm_init_failed',
|
|
78
|
+
'swarm_drain_failed',
|
|
79
|
+
// 502: the panel host's daemon did not answer `docker info`. Only POST /v1/swarm/init answers it
|
|
80
|
+
// today (no general error enum exists), so it lives here with the route that emits it.
|
|
81
|
+
'docker_unreachable',
|
|
82
|
+
// Placement (modules/servicePlacement.ts).
|
|
83
|
+
// 422: switching to Swarm for a service Swarm cannot run, or while Swarm is disabled.
|
|
84
|
+
'swarm_unsupported',
|
|
85
|
+
'swarm_disabled',
|
|
86
|
+
// 400: a placement the service or the chosen nodes cannot take.
|
|
87
|
+
'placement_unsupported',
|
|
88
|
+
// ── end 0.16 T8 ──
|
|
89
|
+
]);
|
|
90
|
+
export type MultiNodeErrorCode = z.infer<typeof multiNodeErrorCode>;
|
|
91
|
+
|
|
92
|
+
/** `GET /v1/servers` → `agent`: the persisted capability cache (advisory). */
|
|
93
|
+
export const serverAgentInfo = z.object({
|
|
94
|
+
/** null = the agent answered without a version (an older release). */
|
|
95
|
+
version: z.string().nullable(),
|
|
96
|
+
capabilities: z.array(z.string()),
|
|
97
|
+
checkedAt: z.string().datetime().nullable(),
|
|
98
|
+
});
|
|
99
|
+
export type ServerAgentInfo = z.infer<typeof serverAgentInfo>;
|
|
100
|
+
|
|
101
|
+
/** `GET /v1/servers` → `terminal` (0.15): what the node's agent offers terminals. */
|
|
102
|
+
export const serverTerminalCapability = z.object({
|
|
103
|
+
host: z.boolean(),
|
|
104
|
+
container: z.boolean(),
|
|
105
|
+
/** Why a capability is missing (an older agent, an unreachable node). */
|
|
106
|
+
reason: z.string().optional(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/** `GET /v1/servers` → `features`: what the node can do today, and why not. */
|
|
110
|
+
export const serverFeatures = z.object({
|
|
111
|
+
nixpacks: z.boolean(),
|
|
112
|
+
railpack: z.boolean(),
|
|
113
|
+
privateClones: z.boolean(),
|
|
114
|
+
volumes: z.boolean(),
|
|
115
|
+
databases: z.boolean(),
|
|
116
|
+
imageTransfer: z.boolean(),
|
|
117
|
+
swarm: z.boolean(),
|
|
118
|
+
/** The "update the node agent" hint when any feature is off. */
|
|
119
|
+
reason: z.string().optional(),
|
|
120
|
+
});
|
|
121
|
+
export type ServerFeatures = z.infer<typeof serverFeatures>;
|
|
122
|
+
|
|
123
|
+
// ── build placement and image transfer (§6) ────────────────────────────────
|
|
124
|
+
|
|
125
|
+
export const BUILD_CONCURRENCY_MIN = 1;
|
|
126
|
+
export const BUILD_CONCURRENCY_MAX = 8;
|
|
127
|
+
export const IMAGE_TRANSFERS_LIMIT_MAX = 100;
|
|
128
|
+
|
|
129
|
+
/** Mirrors `serviceBuildOn` in `@ninedeploy/db`. */
|
|
130
|
+
export const buildOn = z.enum(['target', 'panel', 'server']);
|
|
131
|
+
export type BuildOn = z.infer<typeof buildOn>;
|
|
132
|
+
|
|
133
|
+
/** Mirrors `serviceOrchestrator` in `@ninedeploy/db`. */
|
|
134
|
+
export const serviceOrchestrator = z.enum(['container', 'swarm']);
|
|
135
|
+
export type ServiceOrchestrator = z.infer<typeof serviceOrchestrator>;
|
|
136
|
+
|
|
137
|
+
/** A registry repository path (`team/app`), no host and no tag. */
|
|
138
|
+
export const pushRepository = z
|
|
139
|
+
.string()
|
|
140
|
+
.max(255)
|
|
141
|
+
.regex(/^[a-z0-9]+(?:[._/-][a-z0-9]+)*$/, 'must be a repository path such as team/app');
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* `PUT /v1/services/:id/placement` (operator). Partial: only the named keys
|
|
145
|
+
* change; `null` restores the 0.15 default for that key.
|
|
146
|
+
*/
|
|
147
|
+
export const servicePlacement = z
|
|
148
|
+
.object({
|
|
149
|
+
/** null = `target` (build where the service runs). */
|
|
150
|
+
buildOn: buildOn.nullable(),
|
|
151
|
+
buildServerId: id.nullable(),
|
|
152
|
+
pushRegistrySourceId: id.nullable(),
|
|
153
|
+
pushRepository: pushRepository.nullable(),
|
|
154
|
+
/** null = `container` (plain containers). */
|
|
155
|
+
orchestrator: serviceOrchestrator.nullable(),
|
|
156
|
+
})
|
|
157
|
+
.partial()
|
|
158
|
+
.strict();
|
|
159
|
+
export type ServicePlacementInput = z.infer<typeof servicePlacement>;
|
|
160
|
+
|
|
161
|
+
/** `GET /v1/services/:id/placement`, and `placement` on the service response. */
|
|
162
|
+
export const servicePlacementView = z.object({
|
|
163
|
+
buildOn: buildOn.nullable(),
|
|
164
|
+
buildServerId: id.nullable(),
|
|
165
|
+
pushRegistrySourceId: id.nullable(),
|
|
166
|
+
pushRepository: z.string().nullable(),
|
|
167
|
+
orchestrator: serviceOrchestrator.nullable(),
|
|
168
|
+
});
|
|
169
|
+
export type ServicePlacementView = z.infer<typeof servicePlacementView>;
|
|
170
|
+
|
|
171
|
+
/** The build placement a stored value means: NULL is today's `target`. */
|
|
172
|
+
export function effectiveBuildOn(value: BuildOn | null | undefined): BuildOn {
|
|
173
|
+
return value ?? 'target';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The orchestrator a stored value means: NULL is today's `container`. */
|
|
177
|
+
export function effectiveOrchestrator(value: ServiceOrchestrator | null | undefined): ServiceOrchestrator {
|
|
178
|
+
return value ?? 'container';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** `PATCH /v1/servers/:id` (operator): the build-server role. */
|
|
182
|
+
export const serverRoles = z
|
|
183
|
+
.object({
|
|
184
|
+
isBuildServer: z.boolean(),
|
|
185
|
+
buildConcurrency: z.number().int().min(BUILD_CONCURRENCY_MIN).max(BUILD_CONCURRENCY_MAX),
|
|
186
|
+
})
|
|
187
|
+
.partial()
|
|
188
|
+
.strict();
|
|
189
|
+
export type ServerRolesInput = z.infer<typeof serverRoles>;
|
|
190
|
+
|
|
191
|
+
/** Mirrors `imageTransferMethod` in `@ninedeploy/db`. */
|
|
192
|
+
export const imageTransferMethod = z.enum(['stream', 'registry', 'preload']);
|
|
193
|
+
export type ImageTransferMethod = z.infer<typeof imageTransferMethod>;
|
|
194
|
+
|
|
195
|
+
/** Mirrors `imageTransferStatus` in `@ninedeploy/db`. */
|
|
196
|
+
export const imageTransferStatus = z.enum(['running', 'completed', 'failed']);
|
|
197
|
+
export type ImageTransferStatus = z.infer<typeof imageTransferStatus>;
|
|
198
|
+
|
|
199
|
+
/** One `image_transfers` row as the API returns it. A NULL server is the panel host. */
|
|
200
|
+
export const imageTransfer = z.object({
|
|
201
|
+
id,
|
|
202
|
+
deploymentId: id.nullable(),
|
|
203
|
+
serviceId: id,
|
|
204
|
+
sourceServerId: id.nullable(),
|
|
205
|
+
targetServerId: id.nullable(),
|
|
206
|
+
method: imageTransferMethod,
|
|
207
|
+
imageRef: z.string(),
|
|
208
|
+
imageId: z.string().nullable(),
|
|
209
|
+
bytes: z.number().int().nonnegative(),
|
|
210
|
+
sha256: z.string().nullable(),
|
|
211
|
+
status: imageTransferStatus,
|
|
212
|
+
error: z.string().nullable(),
|
|
213
|
+
startedAt: z.string().datetime(),
|
|
214
|
+
finishedAt: z.string().datetime().nullable(),
|
|
215
|
+
durationMs: z.number().int().nullable(),
|
|
216
|
+
});
|
|
217
|
+
export type ImageTransfer = z.infer<typeof imageTransfer>;
|
|
218
|
+
|
|
219
|
+
/** `GET /v1/services/:id/image-transfers` query. Query strings arrive as text. */
|
|
220
|
+
export const imageTransfersQuery = z
|
|
221
|
+
.object({
|
|
222
|
+
limit: z.coerce.number().int().min(1).max(IMAGE_TRANSFERS_LIMIT_MAX).default(20),
|
|
223
|
+
})
|
|
224
|
+
.strict();
|
|
225
|
+
export type ImageTransfersQuery = z.infer<typeof imageTransfersQuery>;
|
|
226
|
+
|
|
227
|
+
// ── node volumes (§4) ──────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
/** A managed volume name: `nd-svc-*` or `nd-db-*` (bind mounts are never attachable). */
|
|
230
|
+
export const managedVolumeName = z
|
|
231
|
+
.string()
|
|
232
|
+
.max(128)
|
|
233
|
+
.regex(/^nd-(?:svc|db)-[a-z0-9][a-z0-9_.-]*$/, 'must be a managed volume name (nd-svc-* or nd-db-*)');
|
|
234
|
+
|
|
235
|
+
/** `?serverId=` on the volume routes: absent = the panel host (today's response). */
|
|
236
|
+
export const volumeHostQuery = z
|
|
237
|
+
.object({
|
|
238
|
+
serverId: z.coerce.number().int().positive().optional(),
|
|
239
|
+
})
|
|
240
|
+
.strict();
|
|
241
|
+
export type VolumeHostQuery = z.infer<typeof volumeHostQuery>;
|
|
242
|
+
|
|
243
|
+
/** `POST /v1/volumes` (operator). `serverId` null or absent = the panel host. */
|
|
244
|
+
export const volumeCreate = z
|
|
245
|
+
.object({
|
|
246
|
+
name: managedVolumeName,
|
|
247
|
+
serverId: id.nullable().optional(),
|
|
248
|
+
})
|
|
249
|
+
.strict();
|
|
250
|
+
export type VolumeCreateInput = z.infer<typeof volumeCreate>;
|
|
251
|
+
|
|
252
|
+
/** A volume list item with the host it lives on (null = the panel host). */
|
|
253
|
+
export const hostVolumeEntry = volumeEntry.extend({ serverId: id.nullable() });
|
|
254
|
+
export type HostVolumeEntry = z.infer<typeof hostVolumeEntry>;
|
|
255
|
+
|
|
256
|
+
// ── Swarm (§7) ─────────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
/** Step-up password, as on the other step-up routes. */
|
|
259
|
+
const stepUpPassword = z.string().min(1).max(1024).optional();
|
|
260
|
+
|
|
261
|
+
/** `POST /v1/swarm/init` (operator, interactive, step-up). */
|
|
262
|
+
export const swarmInit = z
|
|
263
|
+
.object({
|
|
264
|
+
advertiseAddr: z.union([z.ipv4(), z.ipv6()]),
|
|
265
|
+
password: stepUpPassword,
|
|
266
|
+
})
|
|
267
|
+
.strict();
|
|
268
|
+
export type SwarmInitInput = z.infer<typeof swarmInit>;
|
|
269
|
+
|
|
270
|
+
/** `PUT /v1/swarm/settings` (operator; step-up to enable). */
|
|
271
|
+
export const swarmSettings = z
|
|
272
|
+
.object({
|
|
273
|
+
enabled: z.boolean(),
|
|
274
|
+
password: stepUpPassword,
|
|
275
|
+
})
|
|
276
|
+
.strict();
|
|
277
|
+
export type SwarmSettingsInput = z.infer<typeof swarmSettings>;
|
|
278
|
+
|
|
279
|
+
export const swarmNodeRole = z.enum(['manager', 'worker']);
|
|
280
|
+
export type SwarmNodeRole = z.infer<typeof swarmNodeRole>;
|
|
281
|
+
|
|
282
|
+
/** One Swarm node; `serverId` links it to a NineDeploy node (null = the panel host or a foreign node). */
|
|
283
|
+
export const swarmNode = z.object({
|
|
284
|
+
id: z.string(),
|
|
285
|
+
hostname: z.string(),
|
|
286
|
+
role: swarmNodeRole,
|
|
287
|
+
availability: z.string(),
|
|
288
|
+
state: z.string(),
|
|
289
|
+
serverId: id.nullable(),
|
|
290
|
+
/** T7 (review M2): e.g. the node's agent switched the Docker socket off while the node is still in the swarm. */
|
|
291
|
+
warnings: z.array(z.string()).optional(),
|
|
292
|
+
});
|
|
293
|
+
export type SwarmNode = z.infer<typeof swarmNode>;
|
|
294
|
+
|
|
295
|
+
/** `GET /v1/swarm` (operator). Join tokens are never returned. */
|
|
296
|
+
export const swarmStatus = z.object({
|
|
297
|
+
enabled: z.boolean(),
|
|
298
|
+
/** `docker info` `.Swarm.LocalNodeState`: inactive, pending, active, error, locked. */
|
|
299
|
+
localState: z.string(),
|
|
300
|
+
controlAvailable: z.boolean(),
|
|
301
|
+
managerAddr: z.string().nullable(),
|
|
302
|
+
nodes: z.array(swarmNode),
|
|
303
|
+
/** T7 (review M1b): things the operator must do, e.g. firewall 2377/tcp when the management port binds every interface. */
|
|
304
|
+
warnings: z.array(z.string()).optional(),
|
|
305
|
+
});
|
|
306
|
+
export type SwarmStatus = z.infer<typeof swarmStatus>;
|
|
307
|
+
|
|
308
|
+
/** `GET /v1/services/:id/swarm` (viewer). */
|
|
309
|
+
export const serviceSwarmStatus = z.object({
|
|
310
|
+
/** null = the service has no stack (not on Swarm, or not deployed yet). */
|
|
311
|
+
stack: z.string().nullable(),
|
|
312
|
+
desired: z.number().int().nonnegative(),
|
|
313
|
+
running: z.number().int().nonnegative(),
|
|
314
|
+
tasks: z.array(
|
|
315
|
+
z.object({
|
|
316
|
+
node: z.string(),
|
|
317
|
+
state: z.string(),
|
|
318
|
+
error: z.string().nullable(),
|
|
319
|
+
image: z.string(),
|
|
320
|
+
}),
|
|
321
|
+
),
|
|
322
|
+
});
|
|
323
|
+
export type ServiceSwarmStatus = z.infer<typeof serviceSwarmStatus>;
|
|
324
|
+
|
|
325
|
+
// ── 0.16 T8 surfaces: response shapes for the OpenAPI document ─────────────
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* One `GET /v1/servers` entry (operator). Every field after `createdAt` is
|
|
329
|
+
* additive and absent on older panels: `terminal` (0.15), then the agent
|
|
330
|
+
* capability cache and features, the build-server role, the hosted database
|
|
331
|
+
* count and the Swarm membership recorded on join. Never the agent token.
|
|
332
|
+
*/
|
|
333
|
+
export const serverListItem = z.object({
|
|
334
|
+
id,
|
|
335
|
+
name: z.string(),
|
|
336
|
+
host: z.string(),
|
|
337
|
+
port: z.number().int(),
|
|
338
|
+
/** `online` turns into `offline` at read time when the node was not seen for 5 minutes. */
|
|
339
|
+
status: z.string(),
|
|
340
|
+
lastSeenAt: z.string().datetime().nullable(),
|
|
341
|
+
createdAt: z.string().datetime(),
|
|
342
|
+
terminal: serverTerminalCapability.optional(),
|
|
343
|
+
/** null until a sealed `agent.ping` answered. */
|
|
344
|
+
agent: serverAgentInfo.nullable().optional(),
|
|
345
|
+
features: serverFeatures.optional(),
|
|
346
|
+
isBuildServer: z.boolean().optional(),
|
|
347
|
+
buildConcurrency: z.number().int().min(BUILD_CONCURRENCY_MIN).max(BUILD_CONCURRENCY_MAX).optional(),
|
|
348
|
+
/** Managed databases hosted on the node. */
|
|
349
|
+
databases: z.number().int().nonnegative().optional(),
|
|
350
|
+
/** The node's swarm id once it joined; null = not in the swarm. */
|
|
351
|
+
swarmNodeId: z.string().nullable().optional(),
|
|
352
|
+
swarmRole: swarmNodeRole.nullable().optional(),
|
|
353
|
+
});
|
|
354
|
+
export type ServerListItem = z.infer<typeof serverListItem>;
|