@ninedeploy/sdk 0.10.6 → 0.16.1
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/apiPath.d.ts +5 -0
- package/dist/apiPath.d.ts.map +1 -0
- package/dist/apiPath.js +18 -0
- package/dist/apiPath.js.map +1 -0
- package/dist/client.d.ts +29 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +51 -0
- package/dist/client.js.map +1 -0
- package/dist/databaseImport.d.ts +138 -0
- package/dist/databaseImport.d.ts.map +1 -0
- package/dist/databaseImport.js +126 -0
- package/dist/databaseImport.js.map +1 -0
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +15 -1973
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -641
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +25 -21
- package/dist/manifest.js.map +1 -1
- package/dist/resources/auth.d.ts +282 -0
- package/dist/resources/auth.d.ts.map +1 -0
- package/dist/resources/auth.js +111 -0
- package/dist/resources/auth.js.map +1 -0
- package/dist/resources/databases.d.ts +178 -0
- package/dist/resources/databases.d.ts.map +1 -0
- package/dist/resources/databases.js +82 -0
- package/dist/resources/databases.js.map +1 -0
- package/dist/resources/deploys.d.ts +125 -0
- package/dist/resources/deploys.d.ts.map +1 -0
- package/dist/resources/deploys.js +46 -0
- package/dist/resources/deploys.js.map +1 -0
- package/dist/resources/domains.d.ts +109 -0
- package/dist/resources/domains.d.ts.map +1 -0
- package/dist/resources/domains.js +29 -0
- package/dist/resources/domains.js.map +1 -0
- package/dist/resources/env.d.ts +55 -0
- package/dist/resources/env.d.ts.map +1 -0
- package/dist/resources/env.js +33 -0
- package/dist/resources/env.js.map +1 -0
- package/dist/resources/logs.d.ts +27 -0
- package/dist/resources/logs.d.ts.map +1 -0
- package/dist/resources/logs.js +18 -0
- package/dist/resources/logs.js.map +1 -0
- package/dist/resources/network.d.ts +169 -0
- package/dist/resources/network.d.ts.map +1 -0
- package/dist/resources/network.js +39 -0
- package/dist/resources/network.js.map +1 -0
- package/dist/resources/plugins.d.ts +42 -0
- package/dist/resources/plugins.d.ts.map +1 -0
- package/dist/resources/plugins.js +22 -0
- package/dist/resources/plugins.js.map +1 -0
- package/dist/resources/servers.d.ts +130 -0
- package/dist/resources/servers.d.ts.map +1 -0
- package/dist/resources/servers.js +37 -0
- package/dist/resources/servers.js.map +1 -0
- package/dist/resources/services.d.ts +240 -0
- package/dist/resources/services.d.ts.map +1 -0
- package/dist/resources/services.js +93 -0
- package/dist/resources/services.js.map +1 -0
- package/dist/resources/settings.d.ts +373 -0
- package/dist/resources/settings.d.ts.map +1 -0
- package/dist/resources/settings.js +94 -0
- package/dist/resources/settings.js.map +1 -0
- package/dist/resources/sources.d.ts +87 -0
- package/dist/resources/sources.d.ts.map +1 -0
- package/dist/resources/sources.js +39 -0
- package/dist/resources/sources.js.map +1 -0
- package/dist/resources/system.d.ts +240 -0
- package/dist/resources/system.d.ts.map +1 -0
- package/dist/resources/system.js +86 -0
- package/dist/resources/system.js.map +1 -0
- package/dist/resources/templates.d.ts +36 -0
- package/dist/resources/templates.d.ts.map +1 -0
- package/dist/resources/templates.js +17 -0
- package/dist/resources/templates.js.map +1 -0
- package/dist/resources/terminals.d.ts +60 -0
- package/dist/resources/terminals.d.ts.map +1 -0
- package/dist/resources/terminals.js +35 -0
- package/dist/resources/terminals.js.map +1 -0
- package/dist/resources/traefik.d.ts +93 -0
- package/dist/resources/traefik.d.ts.map +1 -0
- package/dist/resources/traefik.js +39 -0
- package/dist/resources/traefik.js.map +1 -0
- package/dist/resources/volumes.d.ts +114 -0
- package/dist/resources/volumes.d.ts.map +1 -0
- package/dist/resources/volumes.js +47 -0
- package/dist/resources/volumes.js.map +1 -0
- package/dist/terminal.d.ts +98 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +180 -0
- package/dist/terminal.js.map +1 -0
- package/dist/transport.d.ts +61 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +90 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +539 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +5 -4
- package/src/apiPath.ts +23 -0
- package/src/client.ts +70 -0
- package/src/databaseImport.ts +174 -0
- package/src/errors.ts +27 -0
- package/src/index.ts +121 -0
- package/src/manifest.ts +414 -0
- package/src/resources/auth.ts +357 -0
- package/src/resources/databases.ts +227 -0
- package/src/resources/deploys.ts +161 -0
- package/src/resources/domains.ts +149 -0
- package/src/resources/env.ts +84 -0
- package/src/resources/logs.ts +48 -0
- package/src/resources/network.ts +154 -0
- package/src/resources/plugins.ts +52 -0
- package/src/resources/servers.ts +141 -0
- package/src/resources/services.ts +296 -0
- package/src/resources/settings.ts +334 -0
- package/src/resources/sources.ts +131 -0
- package/src/resources/system.ts +281 -0
- package/src/resources/templates.ts +55 -0
- package/src/resources/terminals.ts +112 -0
- package/src/resources/traefik.ts +125 -0
- package/src/resources/volumes.ts +138 -0
- package/src/terminal.ts +254 -0
- package/src/transport.ts +154 -0
- package/src/types.ts +588 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Client surface: servers, swarm, orchestrators.
|
|
2
|
+
// Split out of the former single-file SDK entry (src/index.ts); the public
|
|
3
|
+
// surface is re-exported unchanged from there.
|
|
4
|
+
import type {
|
|
5
|
+
ServerSshTest,
|
|
6
|
+
ServerSshTestResult,
|
|
7
|
+
ServerSshBootstrap,
|
|
8
|
+
ServerBootstrapResult,
|
|
9
|
+
ServerRolesInput,
|
|
10
|
+
SwarmInitInput,
|
|
11
|
+
SwarmSettingsInput,
|
|
12
|
+
SwarmStatus,
|
|
13
|
+
} from '@ninedeploy/schemas';
|
|
14
|
+
import type { ServerListEntry, ServerRolesResult, SwarmJoinResult, SwarmLeaveResult } from '../types.js';
|
|
15
|
+
import type { Transport } from '../transport.js';
|
|
16
|
+
|
|
17
|
+
/** Long Swarm calls: a leave drains for up to 5 minutes; init and join run Docker on two hosts. */
|
|
18
|
+
const SWARM_CALL_TIMEOUT_MS = 10 * 60_000;
|
|
19
|
+
|
|
20
|
+
export interface ServersApi {
|
|
21
|
+
servers: {
|
|
22
|
+
/**
|
|
23
|
+
* 0.15: `terminal` says what the node's agent offers terminals (absent on
|
|
24
|
+
* a 0.14 panel). `container: false` with a `reason` usually means the
|
|
25
|
+
* agent predates v0.15.0.
|
|
26
|
+
*/
|
|
27
|
+
list: () => Promise<ServerListEntry[]>;
|
|
28
|
+
/** Register a server — the agent token + its sha256 are returned exactly once. */
|
|
29
|
+
create: (input: { name: string; host: string; port?: number }) => Promise<{ id: number; token: string; tokenSha256: string; agentCommand: string }>;
|
|
30
|
+
remove: (id: number, options?: { force?: boolean }) => Promise<{ ok: boolean }>;
|
|
31
|
+
test: (id: number) => Promise<{ ok: boolean; status: string }>;
|
|
32
|
+
approve: (id: number) => Promise<{ ok: boolean; status: string }>;
|
|
33
|
+
reject: (id: number) => Promise<{ ok: boolean }>;
|
|
34
|
+
sshTest: (input: ServerSshTest) => Promise<ServerSshTestResult>;
|
|
35
|
+
sshBootstrap: (input: ServerSshBootstrap) => Promise<ServerBootstrapResult>;
|
|
36
|
+
bootstrapLogs: (id: number) => Promise<{ logs: string[] }>;
|
|
37
|
+
/** r467: live host + per-container telemetry from a node's agent. */
|
|
38
|
+
stats: (id: number) => Promise<{
|
|
39
|
+
host: { cpuCores: number; load1: number; memTotalBytes: number; memUsedBytes: number } | null;
|
|
40
|
+
disk: { totalBytes: number; usedBytes: number };
|
|
41
|
+
containers: Array<{ name: string; kind: 'service'; refId: number; refName: string; cpuPct: number; memMb: number; memLimitMb: number }>;
|
|
42
|
+
}>;
|
|
43
|
+
// ── 0.16 T8 surfaces ──
|
|
44
|
+
/** The build-server role (`PATCH /v1/servers/:id`, operator). Partial. */
|
|
45
|
+
update: (id: number, input: ServerRolesInput) => Promise<ServerRolesResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Join the node to the panel host's swarm as a worker (operator). 422
|
|
48
|
+
* `node_agent_outdated` for an older agent, 422 `node_swarm_not_enabled`
|
|
49
|
+
* until the node's owner sets {@link AGENT_SWARM_MANAGER_VAR}, 409
|
|
50
|
+
* `swarm_already_joined` / `swarm_not_manager`, 502
|
|
51
|
+
* `swarm_overlay_unavailable` / `swarm_node_unverified`.
|
|
52
|
+
*/
|
|
53
|
+
swarmJoin: (id: number) => Promise<SwarmJoinResult>;
|
|
54
|
+
/** Drain the node and take it out of the swarm (operator; can take minutes). 409 `swarm_not_joined`. */
|
|
55
|
+
swarmLeave: (id: number) => Promise<SwarmLeaveResult>;
|
|
56
|
+
// ── end 0.16 T8 ──
|
|
57
|
+
};
|
|
58
|
+
// ── end 0.15 T6 ──
|
|
59
|
+
// ── 0.16 T8 surfaces ──
|
|
60
|
+
/**
|
|
61
|
+
* Swarm on the panel host (operator only). `init`, and enabling through
|
|
62
|
+
* `settings`, need step-up: an interactive session plus the account
|
|
63
|
+
* `password` (or a sign-in within 10 minutes). Join tokens are never returned.
|
|
64
|
+
*/
|
|
65
|
+
swarm: {
|
|
66
|
+
get: () => Promise<SwarmStatus>;
|
|
67
|
+
/** `docker swarm init` on the panel host. 409 `swarm_already_active`; 502 `swarm_overlay_unavailable`. */
|
|
68
|
+
init: (input: SwarmInitInput) => Promise<SwarmStatus>;
|
|
69
|
+
/** Enable (step-up; 409 `swarm_not_manager`) or disable Swarm deploys. */
|
|
70
|
+
settings: (input: SwarmSettingsInput) => Promise<SwarmStatus>;
|
|
71
|
+
};
|
|
72
|
+
orchestrators: {
|
|
73
|
+
/** Every `IOrchestrator` the running kernel has registered. G-10 PR-A. */
|
|
74
|
+
list: () => Promise<{
|
|
75
|
+
orchestrators: Array<{ name: string; stacks: Array<{ name: string; serviceCount: number }> }>;
|
|
76
|
+
}>;
|
|
77
|
+
/** The stacks one orchestrator knows about. */
|
|
78
|
+
stacks: (orchestrator: string) => Promise<{
|
|
79
|
+
stacks: Array<{ name: string; serviceCount: number }>;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Stable snapshot of ONE stack. Returns `null` when the orchestrator has
|
|
83
|
+
* no record of it.
|
|
84
|
+
*
|
|
85
|
+
* r036: this used to take only the orchestrator name and hit
|
|
86
|
+
* `/:name/stacks`, which passed that name through as the STACK name — so
|
|
87
|
+
* it answered `null` for every stack not coincidentally named after its
|
|
88
|
+
* orchestrator. The stack now has its own argument and path segment.
|
|
89
|
+
*/
|
|
90
|
+
stackStatus: (orchestrator: string, stack: string) => Promise<{
|
|
91
|
+
name: string;
|
|
92
|
+
services: Array<{ name: string; state: 'running' | 'stopped' | 'partial' | 'unknown'; replicas: number }>;
|
|
93
|
+
appliedAt: string;
|
|
94
|
+
} | null>;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function serversApi(t: Transport): ServersApi {
|
|
99
|
+
const { get, send, request } = t;
|
|
100
|
+
return {
|
|
101
|
+
servers: {
|
|
102
|
+
list: () => get('/v1/servers'),
|
|
103
|
+
create: (input) => send('POST', '/v1/servers', input),
|
|
104
|
+
remove: (id, opts) => send<{ ok: boolean }>('DELETE', `/v1/servers/${id}${opts?.force ? '?force=true' : ''}`),
|
|
105
|
+
test: (id) => send<{ ok: boolean; status: string }>('POST', `/v1/servers/${id}/test`),
|
|
106
|
+
approve: (id) => send<{ ok: boolean; status: string }>('POST', `/v1/servers/${id}/approve`),
|
|
107
|
+
reject: (id) => send<{ ok: boolean }>('POST', `/v1/servers/${id}/reject`),
|
|
108
|
+
sshTest: (input) => send<ServerSshTestResult>('POST', '/v1/servers/ssh-test', input),
|
|
109
|
+
sshBootstrap: (input) => send<ServerBootstrapResult>('POST', '/v1/servers/ssh-bootstrap', input),
|
|
110
|
+
bootstrapLogs: (id) => get<{ logs: string[] }>(`/v1/servers/${id}/bootstrap-logs`),
|
|
111
|
+
stats: (id) => get(`/v1/servers/${id}/stats`),
|
|
112
|
+
// ── 0.16 T8 surfaces ──
|
|
113
|
+
update: (id, input) => send<ServerRolesResult>('PATCH', `/v1/servers/${id}`, input),
|
|
114
|
+
swarmJoin: (id) => request<SwarmJoinResult>(`/v1/servers/${id}/swarm/join`, { method: 'POST' }, SWARM_CALL_TIMEOUT_MS),
|
|
115
|
+
swarmLeave: (id) => request<SwarmLeaveResult>(`/v1/servers/${id}/swarm/leave`, { method: 'POST' }, SWARM_CALL_TIMEOUT_MS),
|
|
116
|
+
// ── end 0.16 T8 ──
|
|
117
|
+
},
|
|
118
|
+
// ── end 0.15 T6 ──
|
|
119
|
+
// ── 0.16 T8 surfaces ──
|
|
120
|
+
swarm: {
|
|
121
|
+
get: () => get<SwarmStatus>('/v1/swarm'),
|
|
122
|
+
init: (input) => request<SwarmStatus>('/v1/swarm/init', { method: 'POST', body: input }, SWARM_CALL_TIMEOUT_MS),
|
|
123
|
+
settings: (input) => send<SwarmStatus>('PUT', '/v1/swarm/settings', input),
|
|
124
|
+
},
|
|
125
|
+
orchestrators: {
|
|
126
|
+
list: () => get<{
|
|
127
|
+
orchestrators: Array<{ name: string; stacks: Array<{ name: string; serviceCount: number }> }>;
|
|
128
|
+
}>('/v1/orchestrators'),
|
|
129
|
+
stacks: (orchestrator) => get<{
|
|
130
|
+
stacks: Array<{ name: string; serviceCount: number }>;
|
|
131
|
+
}>(`/v1/orchestrators/${encodeURIComponent(orchestrator)}/stacks`),
|
|
132
|
+
stackStatus: (orchestrator, stack) => get<{
|
|
133
|
+
name: string;
|
|
134
|
+
services: Array<{ name: string; state: 'running' | 'stopped' | 'partial' | 'unknown'; replicas: number }>;
|
|
135
|
+
appliedAt: string;
|
|
136
|
+
} | null>(
|
|
137
|
+
`/v1/orchestrators/${encodeURIComponent(orchestrator)}/stacks/${encodeURIComponent(stack)}`,
|
|
138
|
+
),
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// Client surface: services, labels, environments, serviceTags, projects, fanout, limits, attachments.
|
|
2
|
+
// Split out of the former single-file SDK entry (src/index.ts); the public
|
|
3
|
+
// surface is re-exported unchanged from there.
|
|
4
|
+
import type {
|
|
5
|
+
Attachment,
|
|
6
|
+
ComposePreviewRequestInput,
|
|
7
|
+
ComposePreviewResponse,
|
|
8
|
+
CreateProjectInput,
|
|
9
|
+
CreateServiceInput,
|
|
10
|
+
Label,
|
|
11
|
+
CreateLabelInput,
|
|
12
|
+
LabelPatchInput,
|
|
13
|
+
Environment,
|
|
14
|
+
EnvironmentCreateInput,
|
|
15
|
+
ProjectEntry,
|
|
16
|
+
ProjectPatchInput,
|
|
17
|
+
Service,
|
|
18
|
+
ServiceTags,
|
|
19
|
+
SetServiceTagsInput,
|
|
20
|
+
SetLimitsInput,
|
|
21
|
+
UpdateServiceInput,
|
|
22
|
+
ServiceGithubFeedbackPatch,
|
|
23
|
+
ServiceGithubLink,
|
|
24
|
+
ServiceGithubLinkPut,
|
|
25
|
+
ServiceGithubStatus,
|
|
26
|
+
ImageTransfer,
|
|
27
|
+
ServicePlacementInput,
|
|
28
|
+
ServicePlacementView,
|
|
29
|
+
ServiceSwarmStatus,
|
|
30
|
+
} from '@ninedeploy/schemas';
|
|
31
|
+
import { apiGetUrl } from '../apiPath.js';
|
|
32
|
+
import type { ApplyManifestResult, ApplyManifestInput, ServiceDetail } from '../types.js';
|
|
33
|
+
import type { Transport } from '../transport.js';
|
|
34
|
+
|
|
35
|
+
/** 0.16.1: one kept upload of a service's source archive. */
|
|
36
|
+
export interface SourceUpload {
|
|
37
|
+
/** First 40 hex chars of the archive's SHA-256 (the deployment's `commitSha`). */
|
|
38
|
+
sha: string;
|
|
39
|
+
format: 'zip' | 'tar.gz';
|
|
40
|
+
bytes: number;
|
|
41
|
+
uploadedAt: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ServicesApi {
|
|
45
|
+
services: {
|
|
46
|
+
/**
|
|
47
|
+
* `query` is appended verbatim. Use `?tagWorkspaceIds=1,2&tagProjectIds=3`
|
|
48
|
+
* to scope the list by tag (each dimension ANDs, members within a
|
|
49
|
+
* dimension OR). The legacy `?projectId=` query is no longer accepted.
|
|
50
|
+
*/
|
|
51
|
+
list: (query?: string) => Promise<Service[]>;
|
|
52
|
+
/** 0.16 T8: the detail response also carries `placement` (absent on older panels). */
|
|
53
|
+
get: (id: number) => Promise<ServiceDetail>;
|
|
54
|
+
create: (input: CreateServiceInput) => Promise<Service>;
|
|
55
|
+
update: (id: number, input: UpdateServiceInput) => Promise<Service>;
|
|
56
|
+
/**
|
|
57
|
+
* Dry-run a compose file before creating anything: blocking reasons,
|
|
58
|
+
* host-privilege warnings, the declared services and the variables the
|
|
59
|
+
* stack will ask for. Admin-only, and writes nothing.
|
|
60
|
+
*/
|
|
61
|
+
composePreview: (input: ComposePreviewRequestInput) => Promise<ComposePreviewResponse>;
|
|
62
|
+
remove: (id: number) => Promise<void>;
|
|
63
|
+
stop: (id: number) => Promise<{ ok: boolean; status: string }>;
|
|
64
|
+
start: (id: number) => Promise<{ ok: boolean; status: string }>;
|
|
65
|
+
restart: (id: number) => Promise<{ ok: boolean; status: string }>;
|
|
66
|
+
logs: (id: number) => Promise<{ lines: string }>;
|
|
67
|
+
clone: (id: number, input?: { name?: string; slug?: string }) => Promise<Service>;
|
|
68
|
+
exportUrl: (id: number) => string;
|
|
69
|
+
/**
|
|
70
|
+
* 0.16.1: send a `.zip` / `.tar.gz` (up to 100 MiB) as the source of a
|
|
71
|
+
* docker service created without a repository or image, and deploy it
|
|
72
|
+
* (`deploy: false` only stores it). A panel older than 0.16.1 answers 404.
|
|
73
|
+
*/
|
|
74
|
+
uploadSource: (
|
|
75
|
+
id: number,
|
|
76
|
+
archive: Uint8Array,
|
|
77
|
+
opts?: { deploy?: boolean },
|
|
78
|
+
) => Promise<SourceUpload & { entries: number; deploymentId: number | null }>;
|
|
79
|
+
/**
|
|
80
|
+
* 0.16.1 deploy status badge (member). `enable` turns it on or rotates its
|
|
81
|
+
* URL; `disable` turns it off. The URL is the badge's only credential.
|
|
82
|
+
*/
|
|
83
|
+
badge: {
|
|
84
|
+
get: (id: number) => Promise<{ enabled: boolean; url: string | null }>;
|
|
85
|
+
enable: (id: number) => Promise<{ enabled: boolean; url: string | null }>;
|
|
86
|
+
disable: (id: number) => Promise<{ enabled: boolean; url: string | null }>;
|
|
87
|
+
};
|
|
88
|
+
/** 0.16.1: the kept uploads (newest first) and whether the service takes uploads. */
|
|
89
|
+
sourceUploads: (id: number) => Promise<{ uploadSourced: boolean; maxBytes: number; uploads: SourceUpload[] }>;
|
|
90
|
+
/** 0.16.1: the panel host IPs a port mapping can be bound to (member), with the operator's label, the primary mark and whether one is dedicated to this service or another; `detected: false` when the host could not be asked. */
|
|
91
|
+
hostAddresses: (id: number) => Promise<{ detected: boolean; addresses: Array<{ ip: string; family: 4 | 6; iface: string; label?: string | null; primary?: boolean; dedicated?: 'self' | 'other' | null }> }>;
|
|
92
|
+
importBundle: (bundle: unknown) => Promise<{ ok: boolean; serviceId: number; slug: string; message: string }>;
|
|
93
|
+
/**
|
|
94
|
+
* `.ninedeploy` manifest endpoints. The server applies
|
|
95
|
+
* build / run / network sections to the service + build
|
|
96
|
+
* config rows (operator > manifest > DB merge semantics);
|
|
97
|
+
* routes / alerts / database reconcile happens at deploy
|
|
98
|
+
* time via the deploy pipeline and is intentionally
|
|
99
|
+
* outside this endpoint's scope.
|
|
100
|
+
*/
|
|
101
|
+
manifest: {
|
|
102
|
+
/**
|
|
103
|
+
* Push a parsed manifest to the panel and reconcile it
|
|
104
|
+
* into the service. Requires the `admin` role on the
|
|
105
|
+
* service. The response echoes the diff so the CLI can
|
|
106
|
+
* render a one-shot summary without a follow-up GET.
|
|
107
|
+
*/
|
|
108
|
+
apply: (serviceId: number, input: ApplyManifestInput) => Promise<ApplyManifestResult>;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* The service's GitHub App link (0.13). `get` is viewer; `feedback` is
|
|
112
|
+
* service admin; `link`, `migrate`, `finalize` and `unlink` are operator.
|
|
113
|
+
* `unlink` answers 409 when the service clones through the App source
|
|
114
|
+
* itself (attach another source first, or disable the link).
|
|
115
|
+
*/
|
|
116
|
+
github: {
|
|
117
|
+
get: (id: number) => Promise<ServiceGithubStatus>;
|
|
118
|
+
link: (id: number, input: ServiceGithubLinkPut) => Promise<{ link: ServiceGithubLink }>;
|
|
119
|
+
feedback: (id: number, input: ServiceGithubFeedbackPatch) => Promise<{ link: ServiceGithubLink }>;
|
|
120
|
+
migrate: (id: number, sourceId: number) => Promise<{ link: ServiceGithubLink }>;
|
|
121
|
+
finalize: (id: number) => Promise<{ link: ServiceGithubLink; webhooksDeactivated: number }>;
|
|
122
|
+
unlink: (id: number) => Promise<{ ok: boolean; sourceId: number | null; webhooksReactivated: number }>;
|
|
123
|
+
};
|
|
124
|
+
// ── 0.16 T8 surfaces ──
|
|
125
|
+
/**
|
|
126
|
+
* Build placement (multi-node): where the image is built, how it travels
|
|
127
|
+
* and which orchestrator runs it. `get` is any seat on the service; `set`
|
|
128
|
+
* is operator only and partial (only the named keys change; `null`
|
|
129
|
+
* restores the 0.15 default). An older node agent answers 422
|
|
130
|
+
* `node_agent_outdated`; switching to Swarm answers 422 `swarm_unsupported`
|
|
131
|
+
* or `swarm_disabled`.
|
|
132
|
+
*/
|
|
133
|
+
placement: {
|
|
134
|
+
get: (id: number) => Promise<ServicePlacementView>;
|
|
135
|
+
set: (id: number, input: ServicePlacementInput) => Promise<ServicePlacementView>;
|
|
136
|
+
};
|
|
137
|
+
/** Image transfers of the service, newest first (`limit` 1-100, default 20). */
|
|
138
|
+
imageTransfers: (id: number, query?: { limit?: number }) => Promise<ImageTransfer[]>;
|
|
139
|
+
/** Swarm tasks of the service; `stack: null` when it is not on Swarm. */
|
|
140
|
+
swarm: (id: number) => Promise<ServiceSwarmStatus>;
|
|
141
|
+
// ── end 0.16 T8 ──
|
|
142
|
+
};
|
|
143
|
+
labels: {
|
|
144
|
+
/** `query` is appended verbatim, e.g. `?workspaceId=1`. */
|
|
145
|
+
list: (query?: string) => Promise<Label[]>;
|
|
146
|
+
create: (input: CreateLabelInput) => Promise<Label>;
|
|
147
|
+
update: (id: number, input: LabelPatchInput) => Promise<Label>;
|
|
148
|
+
remove: (id: number) => Promise<{ ok: boolean }>;
|
|
149
|
+
};
|
|
150
|
+
/** Deployment lanes (production / staging / development) per workspace. */
|
|
151
|
+
environments: {
|
|
152
|
+
list: () => Promise<Environment[]>;
|
|
153
|
+
create: (input: EnvironmentCreateInput) => Promise<Environment>;
|
|
154
|
+
rename: (id: number, name: string) => Promise<Environment>;
|
|
155
|
+
remove: (id: number) => Promise<{ ok: boolean }>;
|
|
156
|
+
};
|
|
157
|
+
serviceTags: {
|
|
158
|
+
/** Read the project's / workspace's / label memberships of a service. */
|
|
159
|
+
get: (serviceId: number) => Promise<ServiceTags>;
|
|
160
|
+
/** Replace the membership in a single round-trip. Empty arrays clear. */
|
|
161
|
+
set: (serviceId: number, input: SetServiceTagsInput) => Promise<ServiceTags>;
|
|
162
|
+
};
|
|
163
|
+
projects: {
|
|
164
|
+
/** `query` is appended verbatim, e.g. `?workspaceId=2` (workspace scoping). */
|
|
165
|
+
list: (query?: string) => Promise<ProjectEntry[]>;
|
|
166
|
+
create: (input: CreateProjectInput) => Promise<ProjectEntry>;
|
|
167
|
+
update: (id: number, input: ProjectPatchInput) => Promise<ProjectEntry>;
|
|
168
|
+
remove: (id: number) => Promise<{ ok: boolean }>;
|
|
169
|
+
};
|
|
170
|
+
fanout: {
|
|
171
|
+
/** The extra nodes an image/Dockerfile release is pushed to. */
|
|
172
|
+
get: (serviceId: number) =>
|
|
173
|
+
Promise<Array<{ serverId: number; runtimeId: string | null; status: string }>>;
|
|
174
|
+
/** Replace the target-node set (operator-only). */
|
|
175
|
+
set: (serviceId: number, serverIds: number[]) =>
|
|
176
|
+
Promise<{ targets: Array<{ serverId: number; runtimeId: string | null; status: string }> }>;
|
|
177
|
+
};
|
|
178
|
+
limits: {
|
|
179
|
+
setService: (serviceId: number, input: SetLimitsInput) => Promise<{ cpuShares: number; cpuLimitMilli: number; memLimitMb: number; liveApplied: boolean }>;
|
|
180
|
+
setDatabase: (databaseId: number, input: SetLimitsInput) => Promise<{ cpuShares: number; cpuLimitMilli: number; memLimitMb: number }>;
|
|
181
|
+
};
|
|
182
|
+
attachments: {
|
|
183
|
+
list: (serviceId: number) => Promise<Attachment[]>;
|
|
184
|
+
create: (serviceId: number, input: { databaseId: number; envAlias?: string; reuseExisting?: boolean }) => Promise<Attachment>;
|
|
185
|
+
remove: (serviceId: number, attachmentId: number) => Promise<void>;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function servicesApi(t: Transport): ServicesApi {
|
|
190
|
+
const { get, send, request } = t;
|
|
191
|
+
return {
|
|
192
|
+
services: {
|
|
193
|
+
list: (query) => get<Service[]>(`/v1/services${query ?? ''}`),
|
|
194
|
+
get: (id) => get<ServiceDetail>(`/v1/services/${id}`),
|
|
195
|
+
create: (input) => send<Service>('POST', '/v1/services', input),
|
|
196
|
+
update: (id, input) => send<Service>('PATCH', `/v1/services/${id}`, input),
|
|
197
|
+
composePreview: (input) => send<ComposePreviewResponse>('POST', '/v1/services/compose/preview', input),
|
|
198
|
+
remove: async (id) => {
|
|
199
|
+
await request(`/v1/services/${id}`, { method: 'DELETE' });
|
|
200
|
+
},
|
|
201
|
+
stop: (id) => send<{ ok: boolean; status: string }>('POST', `/v1/services/${id}/stop`),
|
|
202
|
+
start: (id) => send<{ ok: boolean; status: string }>('POST', `/v1/services/${id}/start`),
|
|
203
|
+
restart: (id) => send<{ ok: boolean; status: string }>('POST', `/v1/services/${id}/restart`),
|
|
204
|
+
logs: (id) => get<{ lines: string }>(`/v1/services/${id}/logs`),
|
|
205
|
+
clone: (id, input) => send<Service>('POST', `/v1/services/${id}/clone`, input ?? {}),
|
|
206
|
+
exportUrl: (id) => `/v1/services/${id}/export`,
|
|
207
|
+
uploadSource: (id, archive, opts) =>
|
|
208
|
+
request<SourceUpload & { entries: number; deploymentId: number | null }>(
|
|
209
|
+
`/v1/services/${id}/source${opts?.deploy === false ? '?deploy=false' : ''}`,
|
|
210
|
+
{ method: 'POST', raw: archive },
|
|
211
|
+
// An upload of up to 100 MiB may outlast the default budget.
|
|
212
|
+
10 * 60_000,
|
|
213
|
+
),
|
|
214
|
+
badge: {
|
|
215
|
+
get: (id) => get<{ enabled: boolean; url: string | null }>(`/v1/services/${id}/badge`),
|
|
216
|
+
enable: (id) => send<{ enabled: boolean; url: string | null }>('POST', `/v1/services/${id}/badge`),
|
|
217
|
+
disable: (id) => request<{ enabled: boolean; url: string | null }>(`/v1/services/${id}/badge`, { method: 'DELETE' }),
|
|
218
|
+
},
|
|
219
|
+
sourceUploads: (id) => get<{ uploadSourced: boolean; maxBytes: number; uploads: SourceUpload[] }>(`/v1/services/${id}/source`),
|
|
220
|
+
hostAddresses: (id) => get<{ detected: boolean; addresses: Array<{ ip: string; family: 4 | 6; iface: string; label?: string | null; primary?: boolean; dedicated?: 'self' | 'other' | null }> }>(`/v1/services/${id}/host-addresses`),
|
|
221
|
+
importBundle: (bundle) => send<{ ok: boolean; serviceId: number; slug: string; message: string }>('POST', '/v1/services/import', bundle),
|
|
222
|
+
manifest: {
|
|
223
|
+
apply: (serviceId, input) =>
|
|
224
|
+
send<ApplyManifestResult>('POST', `/v1/services/${serviceId}/manifest/apply`, input),
|
|
225
|
+
},
|
|
226
|
+
github: {
|
|
227
|
+
get: (id) => get<ServiceGithubStatus>(`/v1/services/${id}/github`),
|
|
228
|
+
link: (id, input) => send<{ link: ServiceGithubLink }>('PUT', `/v1/services/${id}/github`, input),
|
|
229
|
+
feedback: (id, input) => send<{ link: ServiceGithubLink }>('PATCH', `/v1/services/${id}/github/feedback`, input),
|
|
230
|
+
migrate: (id, sourceId) => send<{ link: ServiceGithubLink }>('POST', `/v1/services/${id}/github/migrate`, { sourceId }),
|
|
231
|
+
finalize: (id) => send<{ link: ServiceGithubLink; webhooksDeactivated: number }>('POST', `/v1/services/${id}/github/finalize`),
|
|
232
|
+
unlink: (id) => send<{ ok: boolean; sourceId: number | null; webhooksReactivated: number }>('DELETE', `/v1/services/${id}/github`),
|
|
233
|
+
},
|
|
234
|
+
// ── 0.16 T8 surfaces ──
|
|
235
|
+
placement: {
|
|
236
|
+
get: (id) => get<ServicePlacementView>(`/v1/services/${id}/placement`),
|
|
237
|
+
set: (id, input) => send<ServicePlacementView>('PUT', `/v1/services/${id}/placement`, input),
|
|
238
|
+
},
|
|
239
|
+
imageTransfers: (id, query) => get<ImageTransfer[]>(apiGetUrl(`/v1/services/${id}/image-transfers`, { ...query })),
|
|
240
|
+
swarm: (id) => get<ServiceSwarmStatus>(`/v1/services/${id}/swarm`),
|
|
241
|
+
// ── end 0.16 T8 ──
|
|
242
|
+
},
|
|
243
|
+
labels: {
|
|
244
|
+
list: (query) => get<Label[]>(`/v1/labels${query ?? ''}`),
|
|
245
|
+
create: (input) => send<Label>('POST', '/v1/labels', input),
|
|
246
|
+
update: (id, input) => send<Label>('PATCH', `/v1/labels/${id}`, input),
|
|
247
|
+
remove: (id) => send<{ ok: boolean }>('DELETE', `/v1/labels/${id}`),
|
|
248
|
+
},
|
|
249
|
+
environments: {
|
|
250
|
+
list: () => get<Environment[]>('/v1/environments'),
|
|
251
|
+
create: (input) => send<Environment>('POST', '/v1/environments', input),
|
|
252
|
+
rename: (id, name) => send<Environment>('PATCH', `/v1/environments/${id}`, { name }),
|
|
253
|
+
remove: (id) => send<{ ok: boolean }>('DELETE', `/v1/environments/${id}`),
|
|
254
|
+
},
|
|
255
|
+
serviceTags: {
|
|
256
|
+
get: (id) => get<ServiceTags>(`/v1/services/${id}/tags`),
|
|
257
|
+
set: (id, input) => send<ServiceTags>('PUT', `/v1/services/${id}/tags`, input),
|
|
258
|
+
},
|
|
259
|
+
projects: {
|
|
260
|
+
list: (query) => get<ProjectEntry[]>(`/v1/projects${query ?? ''}`),
|
|
261
|
+
create: (input) => send<ProjectEntry>('POST', '/v1/projects', input),
|
|
262
|
+
update: (id, input) => send<ProjectEntry>('PATCH', `/v1/projects/${id}`, input),
|
|
263
|
+
remove: (id) => send<{ ok: boolean }>('DELETE', `/v1/projects/${id}`),
|
|
264
|
+
},
|
|
265
|
+
fanout: {
|
|
266
|
+
// r201: the route answers `{ targets: [...] }` (like PATCH); the SDK
|
|
267
|
+
// typed and returned it as a bare array, so the panel's fan-out card
|
|
268
|
+
// called `.map` on an object and crashed whenever it rendered.
|
|
269
|
+
get: async (serviceId) =>
|
|
270
|
+
(
|
|
271
|
+
await get<{ targets: Array<{ serverId: number; runtimeId: string | null; status: string }> }>(
|
|
272
|
+
`/v1/services/${serviceId}/targets`,
|
|
273
|
+
)
|
|
274
|
+
).targets,
|
|
275
|
+
set: (serviceId, serverIds) =>
|
|
276
|
+
send<{ targets: Array<{ serverId: number; runtimeId: string | null; status: string }> }>(
|
|
277
|
+
'PATCH',
|
|
278
|
+
`/v1/services/${serviceId}/targets`,
|
|
279
|
+
{ serverIds },
|
|
280
|
+
),
|
|
281
|
+
},
|
|
282
|
+
limits: {
|
|
283
|
+
setService: (serviceId, input) =>
|
|
284
|
+
send<{ cpuShares: number; cpuLimitMilli: number; memLimitMb: number; liveApplied: boolean }>('PATCH', `/v1/services/${serviceId}/limits`, input),
|
|
285
|
+
setDatabase: (databaseId, input) =>
|
|
286
|
+
send<{ cpuShares: number; cpuLimitMilli: number; memLimitMb: number }>('PATCH', `/v1/databases/${databaseId}/limits`, input),
|
|
287
|
+
},
|
|
288
|
+
attachments: {
|
|
289
|
+
list: (serviceId) => get<Attachment[]>(`/v1/services/${serviceId}/attachments`),
|
|
290
|
+
create: (serviceId, input) => send<Attachment>('POST', `/v1/services/${serviceId}/attachments`, input),
|
|
291
|
+
remove: async (serviceId, attachmentId) => {
|
|
292
|
+
await request(`/v1/services/${serviceId}/attachments/${attachmentId}`, { method: 'DELETE' });
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|