@deveye/types 0.15.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 -0
- package/README.md +23 -0
- package/package.json +68 -0
- package/src/domain/audience.ts +549 -0
- package/src/domain/backup.ts +355 -0
- package/src/domain/credential.ts +55 -0
- package/src/domain/database.ts +467 -0
- package/src/domain/deploy.ts +231 -0
- package/src/domain/device.ts +172 -0
- package/src/domain/deviceFiles.ts +84 -0
- package/src/domain/deviceLogs.ts +82 -0
- package/src/domain/featureRegistry.ts +392 -0
- package/src/domain/finance.ts +477 -0
- package/src/domain/git.ts +419 -0
- package/src/domain/home.ts +314 -0
- package/src/domain/live.ts +272 -0
- package/src/domain/logs.ts +117 -0
- package/src/domain/mail.ts +394 -0
- package/src/domain/metrics.ts +127 -0
- package/src/domain/note.ts +202 -0
- package/src/domain/notifications.ts +268 -0
- package/src/domain/packages.ts +35 -0
- package/src/domain/password.ts +36 -0
- package/src/domain/presence.ts +21 -0
- package/src/domain/project.ts +168 -0
- package/src/domain/projectBoard.ts +130 -0
- package/src/domain/projectChat.ts +46 -0
- package/src/domain/projectHistory.ts +82 -0
- package/src/domain/projectLink.ts +87 -0
- package/src/domain/projectPlan.ts +68 -0
- package/src/domain/report.ts +492 -0
- package/src/domain/role.ts +8 -0
- package/src/domain/secrecy.ts +66 -0
- package/src/domain/sentinel.ts +623 -0
- package/src/domain/sharing.ts +186 -0
- package/src/domain/syncProtocol.ts +116 -0
- package/src/domain/twoFactor.ts +40 -0
- package/src/domain/uptime.ts +216 -0
- package/src/domain/user.ts +141 -0
- package/src/domain/workspace.ts +56 -0
- package/src/domain/workspaceRole.ts +251 -0
- package/src/features/admin.ts +112 -0
- package/src/features/audience.ts +275 -0
- package/src/features/backup.ts +230 -0
- package/src/features/database.ts +461 -0
- package/src/features/deploy.ts +245 -0
- package/src/features/device.ts +292 -0
- package/src/features/deviceFiles.ts +83 -0
- package/src/features/deviceLogs.ts +36 -0
- package/src/features/deviceTerminal.ts +57 -0
- package/src/features/finance.ts +360 -0
- package/src/features/git.ts +368 -0
- package/src/features/home.ts +32 -0
- package/src/features/live.ts +113 -0
- package/src/features/logs.ts +86 -0
- package/src/features/mail.ts +374 -0
- package/src/features/metrics.ts +185 -0
- package/src/features/note.ts +189 -0
- package/src/features/notify.ts +164 -0
- package/src/features/password.ts +67 -0
- package/src/features/project.ts +709 -0
- package/src/features/registry.ts +103 -0
- package/src/features/secrecy.ts +120 -0
- package/src/features/sentinel.ts +233 -0
- package/src/features/sharing.ts +79 -0
- package/src/features/twoFactor.ts +47 -0
- package/src/features/uptime.ts +186 -0
- package/src/features/user.ts +91 -0
- package/src/features/workspace.ts +200 -0
- package/src/http/auth.ts +94 -0
- package/src/http/device.ts +222 -0
- package/src/http/status.ts +45 -0
- package/src/index.ts +1700 -0
- package/src/protocol/agent.ts +1171 -0
- package/src/protocol/envelope.ts +46 -0
- package/src/protocol/error.ts +27 -0
- package/src/protocol/result.ts +17 -0
- package/src/protocol/version.ts +6 -0
- package/src/sdk/client-ambient.d.ts +238 -0
- package/src/sdk/client.ts +66 -0
- package/src/sdk/ids.ts +25 -0
- package/src/sdk/index.ts +11 -0
- package/src/sdk/manifest.ts +326 -0
- package/src/sdk/providers.ts +48 -0
- package/src/sdk/server.ts +378 -0
- package/src/sdk/testing.ts +179 -0
- package/src/utils/version.ts +28 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
AUDIENCE_BREAKDOWN_MAX,
|
|
4
|
+
AUDIENCE_FUNNEL_MAX_STEPS,
|
|
5
|
+
AUDIENCE_FUNNEL_NAME_MAX_LENGTH,
|
|
6
|
+
AUDIENCE_MAX_ORIGINS,
|
|
7
|
+
AUDIENCE_ORIGIN_MAX_LENGTH,
|
|
8
|
+
AUDIENCE_RETENTION_MAX_DAYS,
|
|
9
|
+
AUDIENCE_RETENTION_MIN_DAYS,
|
|
10
|
+
AUDIENCE_SITE_DESCRIPTION_MAX_LENGTH,
|
|
11
|
+
AUDIENCE_SITE_NAME_MAX_LENGTH,
|
|
12
|
+
audienceActivitySchema,
|
|
13
|
+
audienceBreakdownItemSchema,
|
|
14
|
+
audienceDimensionSchema,
|
|
15
|
+
audienceFunnelSchema,
|
|
16
|
+
audienceFunnelStepDraftSchema,
|
|
17
|
+
audienceLiveSchema,
|
|
18
|
+
audienceOverviewSchema,
|
|
19
|
+
audiencePlatformSchema,
|
|
20
|
+
audienceRangeSchema,
|
|
21
|
+
audienceVisitorModeSchema,
|
|
22
|
+
audienceSiteSchema,
|
|
23
|
+
audienceUsageSchema
|
|
24
|
+
} from '../domain/audience';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Commandes de l'audience d'un espace.
|
|
28
|
+
*
|
|
29
|
+
* Préfixe unique `audience.`, comme `git.`, `database.` et `project.` — d'où le
|
|
30
|
+
* camelCase derrière le point.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ Conséquence à connaître : le filet de démarrage (`MUTATION_VERB` dans
|
|
33
|
+
* `src/features/_topics.ts`) cherche un verbe **juste après le point**. Il ne
|
|
34
|
+
* verra donc **aucune** de ces commandes, et un `mutates` oublié ne produira
|
|
35
|
+
* aucun avertissement. Il se relit à la main.
|
|
36
|
+
*
|
|
37
|
+
* ## Ce qui n'est pas ici
|
|
38
|
+
*
|
|
39
|
+
* L'**ingestion** n'est pas une commande. Elle entre par HTTP, sans session,
|
|
40
|
+
* depuis des machines qui ne connaissent pas DevEye — voir `POST /api/t/b`. Ces
|
|
41
|
+
* commandes-ci ne font que *lire* ce qu'elle a écrit, et *déclarer* les sites.
|
|
42
|
+
*
|
|
43
|
+
* L'espace visé n'apparaît dans aucune entrée : il voyage sur l'enveloppe WS et
|
|
44
|
+
* le dispatcheur le résout, appartenance vérifiée, avant le handler.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const siteId = z.number().int().positive();
|
|
48
|
+
|
|
49
|
+
/** Ce qu'on peut régler sur un site — identique à l'ajout et à la modification. */
|
|
50
|
+
const siteBody = {
|
|
51
|
+
name: z.string().min(1).max(AUDIENCE_SITE_NAME_MAX_LENGTH),
|
|
52
|
+
description: z.string().max(AUDIENCE_SITE_DESCRIPTION_MAX_LENGTH),
|
|
53
|
+
platform: audiencePlatformSchema,
|
|
54
|
+
visitorMode: audienceVisitorModeSchema,
|
|
55
|
+
/**
|
|
56
|
+
* Les hôtes autorisés à écrire. Le client envoie des hôtes nus
|
|
57
|
+
* (`exemple.fr`) ou des origines complètes (`https://exemple.fr`) : le
|
|
58
|
+
* serveur normalise, parce que c'est lui qui compare.
|
|
59
|
+
*/
|
|
60
|
+
origins: z.array(z.string().max(AUDIENCE_ORIGIN_MAX_LENGTH)).max(AUDIENCE_MAX_ORIGINS),
|
|
61
|
+
active: z.boolean(),
|
|
62
|
+
retentionDays: z
|
|
63
|
+
.number()
|
|
64
|
+
.int()
|
|
65
|
+
.min(AUDIENCE_RETENTION_MIN_DAYS)
|
|
66
|
+
.max(AUDIENCE_RETENTION_MAX_DAYS)
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// ------------------------------------------------------------------ sites
|
|
70
|
+
|
|
71
|
+
/** Le nombre de sites de l'espace, pour la tuile de l'accueil. */
|
|
72
|
+
export const audienceCount = {
|
|
73
|
+
command: 'audience.count' as const,
|
|
74
|
+
input: z.object({}),
|
|
75
|
+
output: z.object({ count: z.number().int().nonnegative() })
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Les sites de l'espace, dans l'ordre de l'utilisateur. */
|
|
79
|
+
export const audienceList = {
|
|
80
|
+
command: 'audience.list' as const,
|
|
81
|
+
input: z.object({}),
|
|
82
|
+
output: z.object({ sites: z.array(audienceSiteSchema) })
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Un site, avec les projets qui le suivent et l'adresse de sa balise.
|
|
87
|
+
*
|
|
88
|
+
* `ingestOrigin` vient du serveur (`AUDIENCE_ORIGIN`, à défaut `PUBLIC_ORIGIN`)
|
|
89
|
+
* et non de l'origine du navigateur : l'application est derrière le VPN, alors
|
|
90
|
+
* que l'ingestion doit être joignable sans lui — les deux adresses diffèrent
|
|
91
|
+
* donc par construction. La déduire côté client aurait donné une balise juste en
|
|
92
|
+
* développement et fausse en production, ce qui est le pire des deux mondes.
|
|
93
|
+
*/
|
|
94
|
+
export const audienceGet = {
|
|
95
|
+
command: 'audience.get' as const,
|
|
96
|
+
input: z.object({ siteId }),
|
|
97
|
+
output: z.object({
|
|
98
|
+
site: audienceSiteSchema,
|
|
99
|
+
usage: z.array(audienceUsageSchema),
|
|
100
|
+
ingestOrigin: z.string()
|
|
101
|
+
})
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Déclare un site. La clé publique est **engendrée par le serveur** : la laisser
|
|
106
|
+
* choisir permettrait de viser celle d'un site existant d'un autre espace.
|
|
107
|
+
*/
|
|
108
|
+
export const audienceSiteAdd = {
|
|
109
|
+
command: 'audience.siteAdd' as const,
|
|
110
|
+
input: z.object(siteBody),
|
|
111
|
+
output: z.object({ site: audienceSiteSchema })
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const audienceSiteUpdate = {
|
|
115
|
+
command: 'audience.siteUpdate' as const,
|
|
116
|
+
input: z.object({ siteId, ...siteBody }),
|
|
117
|
+
output: z.object({ site: audienceSiteSchema })
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Supprime un site **et tout son historique**.
|
|
122
|
+
*
|
|
123
|
+
* Contrairement aux projets, rien ne s'archive ici : un site retiré n'a pas de
|
|
124
|
+
* seconde vie, et garder des millions d'événements orphelins pour un objet que
|
|
125
|
+
* plus rien ne nomme ne rendrait service à personne. Les liaisons de projet
|
|
126
|
+
* tombent avec (`CASCADE`), les projets eux-mêmes ne bougent pas.
|
|
127
|
+
*/
|
|
128
|
+
export const audienceSiteRemove = {
|
|
129
|
+
command: 'audience.siteRemove' as const,
|
|
130
|
+
input: z.object({ siteId }),
|
|
131
|
+
output: z.object({ ok: z.literal(true) })
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Renouvelle la clé publique.
|
|
136
|
+
*
|
|
137
|
+
* Le geste utile quand une clé s'est retrouvée là où elle n'aurait pas dû —
|
|
138
|
+
* dépôt public, capture d'écran. L'ancienne cesse d'entrer **immédiatement** ;
|
|
139
|
+
* l'historique déjà collecté, lui, reste : il a été mesuré, il est vrai.
|
|
140
|
+
*/
|
|
141
|
+
export const audienceSiteRotateKey = {
|
|
142
|
+
command: 'audience.siteRotateKey' as const,
|
|
143
|
+
input: z.object({ siteId }),
|
|
144
|
+
output: z.object({ site: audienceSiteSchema })
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const audienceReorder = {
|
|
148
|
+
command: 'audience.reorder' as const,
|
|
149
|
+
input: z.object({ siteIds: z.array(siteId).min(1) }),
|
|
150
|
+
output: z.object({ ok: z.literal(true) })
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// ------------------------------------------------------------ statistiques
|
|
154
|
+
|
|
155
|
+
/** Le bandeau, la comparaison à la période précédente, et la courbe. */
|
|
156
|
+
export const audienceOverview = {
|
|
157
|
+
command: 'audience.overview' as const,
|
|
158
|
+
input: z.object({ siteId, range: audienceRangeSchema }),
|
|
159
|
+
output: audienceOverviewSchema
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Un classement sur l'axe demandé.
|
|
164
|
+
*
|
|
165
|
+
* Une commande pour les neuf axes plutôt que neuf commandes : ils rendent tous
|
|
166
|
+
* la même forme, se lisent par la même requête, et l'écran en affiche plusieurs
|
|
167
|
+
* côte à côte. Neuf entrées auraient divergé au premier ajustement.
|
|
168
|
+
*/
|
|
169
|
+
export const audienceBreakdown = {
|
|
170
|
+
command: 'audience.breakdown' as const,
|
|
171
|
+
input: z.object({
|
|
172
|
+
siteId,
|
|
173
|
+
range: audienceRangeSchema,
|
|
174
|
+
dimension: audienceDimensionSchema,
|
|
175
|
+
limit: z.number().int().min(1).max(AUDIENCE_BREAKDOWN_MAX).optional()
|
|
176
|
+
}),
|
|
177
|
+
output: z.object({ items: z.array(audienceBreakdownItemSchema) })
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** La carte jour × heure, en heure **locale du visiteur**, et les fuseaux. */
|
|
181
|
+
export const audienceActivity = {
|
|
182
|
+
command: 'audience.activity' as const,
|
|
183
|
+
input: z.object({ siteId, range: audienceRangeSchema }),
|
|
184
|
+
output: audienceActivitySchema
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Qui est là en ce moment (cinq dernières minutes).
|
|
189
|
+
*
|
|
190
|
+
* Volontairement séparée de `overview` : elle est minuscule, elle se relit
|
|
191
|
+
* souvent, et la faire voyager avec le bandeau obligerait à recalculer tout
|
|
192
|
+
* l'agrégat pour rafraîchir un compteur.
|
|
193
|
+
*/
|
|
194
|
+
export const audienceLive = {
|
|
195
|
+
command: 'audience.live' as const,
|
|
196
|
+
input: z.object({ siteId }),
|
|
197
|
+
output: audienceLiveSchema
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// ------------------------------------------------------------ entonnoirs
|
|
201
|
+
|
|
202
|
+
const funnelId = z.number().int().positive();
|
|
203
|
+
|
|
204
|
+
/** Les marches, dans l'ordre. Un entonnoir d'une seule marche n'en est pas un. */
|
|
205
|
+
const steps = z.array(audienceFunnelStepDraftSchema).min(2).max(AUDIENCE_FUNNEL_MAX_STEPS);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Les entonnoirs d'un site, **avec leurs chiffres** sur la fenêtre demandée.
|
|
209
|
+
*
|
|
210
|
+
* Définitions et mesures dans la même réponse : l'écran n'affiche jamais l'une
|
|
211
|
+
* sans l'autre, et les séparer aurait fait deux allers-retours pour dessiner un
|
|
212
|
+
* seul graphique. Les entonnoirs se comptent en unités, pas en milliers.
|
|
213
|
+
*/
|
|
214
|
+
export const audienceFunnelList = {
|
|
215
|
+
command: 'audience.funnelList' as const,
|
|
216
|
+
input: z.object({ siteId, range: audienceRangeSchema }),
|
|
217
|
+
output: z.object({ funnels: z.array(audienceFunnelSchema) })
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Définit un entonnoir à partir de ce que le site a **déjà** émis.
|
|
222
|
+
*
|
|
223
|
+
* C'est tout le découpage : le site pose des signaux nommés, l'entonnoir se
|
|
224
|
+
* compose ici. Mesurer autre chose ne demande donc aucun redéploiement — ni
|
|
225
|
+
* même de prévenir qui que ce soit.
|
|
226
|
+
*/
|
|
227
|
+
export const audienceFunnelAdd = {
|
|
228
|
+
command: 'audience.funnelAdd' as const,
|
|
229
|
+
input: z.object({
|
|
230
|
+
siteId,
|
|
231
|
+
name: z.string().min(1).max(AUDIENCE_FUNNEL_NAME_MAX_LENGTH),
|
|
232
|
+
steps
|
|
233
|
+
}),
|
|
234
|
+
output: z.object({ funnelId })
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export const audienceFunnelUpdate = {
|
|
238
|
+
command: 'audience.funnelUpdate' as const,
|
|
239
|
+
input: z.object({
|
|
240
|
+
funnelId,
|
|
241
|
+
name: z.string().min(1).max(AUDIENCE_FUNNEL_NAME_MAX_LENGTH),
|
|
242
|
+
steps
|
|
243
|
+
}),
|
|
244
|
+
output: z.object({ ok: z.literal(true) })
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Supprime un entonnoir. **Aucune mesure n'est perdue** : un entonnoir n'est
|
|
249
|
+
* qu'une lecture des événements déjà là, jamais une collecte à part. Le
|
|
250
|
+
* recréer à l'identique rendrait exactement les mêmes chiffres.
|
|
251
|
+
*/
|
|
252
|
+
export const audienceFunnelRemove = {
|
|
253
|
+
command: 'audience.funnelRemove' as const,
|
|
254
|
+
input: z.object({ funnelId }),
|
|
255
|
+
output: z.object({ ok: z.literal(true) })
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const audienceCommands = [
|
|
259
|
+
audienceCount,
|
|
260
|
+
audienceList,
|
|
261
|
+
audienceGet,
|
|
262
|
+
audienceSiteAdd,
|
|
263
|
+
audienceSiteUpdate,
|
|
264
|
+
audienceSiteRemove,
|
|
265
|
+
audienceSiteRotateKey,
|
|
266
|
+
audienceReorder,
|
|
267
|
+
audienceOverview,
|
|
268
|
+
audienceBreakdown,
|
|
269
|
+
audienceActivity,
|
|
270
|
+
audienceLive,
|
|
271
|
+
audienceFunnelList,
|
|
272
|
+
audienceFunnelAdd,
|
|
273
|
+
audienceFunnelUpdate,
|
|
274
|
+
audienceFunnelRemove
|
|
275
|
+
] as const;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
BACKUP_ACCESS_KEY_MAX,
|
|
4
|
+
BACKUP_BUCKET_MAX,
|
|
5
|
+
BACKUP_DESTINATION_NAME_MAX,
|
|
6
|
+
BACKUP_ENDPOINT_MAX,
|
|
7
|
+
BACKUP_JOB_NAME_MAX,
|
|
8
|
+
BACKUP_PATH_MAX,
|
|
9
|
+
BACKUP_SECRET_MAX,
|
|
10
|
+
backupDestinationKindSchema,
|
|
11
|
+
backupDestinationProbeSchema,
|
|
12
|
+
backupDestinationSchema,
|
|
13
|
+
backupEncryptionSchema,
|
|
14
|
+
backupJobSchema,
|
|
15
|
+
backupRunSchema,
|
|
16
|
+
backupScheduleKindSchema,
|
|
17
|
+
backupSourceCandidateSchema,
|
|
18
|
+
backupSourceKindSchema
|
|
19
|
+
} from '../domain/backup';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Commandes des sauvegardes.
|
|
23
|
+
*
|
|
24
|
+
* Deux moitiés qui ne se recouvrent pas: les **destinations** (où) et les
|
|
25
|
+
* **travaux** (quoi, quand, combien de copies). Tout est à l'étage ouvert, donc
|
|
26
|
+
* aucune de ces commandes ne demande de session déverrouillée — c'est la
|
|
27
|
+
* condition pour qu'une sauvegarde parte à 3 h du matin.
|
|
28
|
+
*
|
|
29
|
+
* `backup.deveyeDump` n'existe pas comme commande: la base de DevEye se
|
|
30
|
+
* sauvegarde par un travail de source `deveye` comme les autres, et il n'y a
|
|
31
|
+
* aucune raison de lui inventer un chemin à part.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const destinationId = z.number().int().positive();
|
|
35
|
+
const jobId = z.number().int().positive();
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------- destinations
|
|
38
|
+
|
|
39
|
+
/** Les destinations de l'espace, dans l'ordre où elles ont été déclarées. */
|
|
40
|
+
export const backupDestinationList = {
|
|
41
|
+
command: 'backup.destinationList' as const,
|
|
42
|
+
input: z.object({}),
|
|
43
|
+
output: z.object({ destinations: z.array(backupDestinationSchema) })
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Déclare une destination.
|
|
48
|
+
*
|
|
49
|
+
* Un seul schéma pour les trois genres, avec les champs des deux autres à
|
|
50
|
+
* `null`: la forme alternative (trois commandes, ou une union discriminée)
|
|
51
|
+
* obligerait l'écran à trois formulaires là où il n'en a qu'un, dont les champs
|
|
52
|
+
* apparaissent selon le genre choisi.
|
|
53
|
+
*/
|
|
54
|
+
export const backupDestinationAdd = {
|
|
55
|
+
command: 'backup.destinationAdd' as const,
|
|
56
|
+
input: z.object({
|
|
57
|
+
kind: backupDestinationKindSchema,
|
|
58
|
+
name: z.string().min(1).max(BACKUP_DESTINATION_NAME_MAX),
|
|
59
|
+
deviceId: z.uuid().nullable(),
|
|
60
|
+
path: z.string().max(BACKUP_PATH_MAX),
|
|
61
|
+
endpoint: z.string().max(BACKUP_ENDPOINT_MAX).nullable(),
|
|
62
|
+
region: z.string().max(64).nullable(),
|
|
63
|
+
bucket: z.string().max(BACKUP_BUCKET_MAX).nullable(),
|
|
64
|
+
accessKeyId: z.string().max(BACKUP_ACCESS_KEY_MAX).nullable(),
|
|
65
|
+
secret: z.string().max(BACKUP_SECRET_MAX).nullable(),
|
|
66
|
+
pathStyle: z.boolean()
|
|
67
|
+
}),
|
|
68
|
+
output: z.object({ destination: backupDestinationSchema })
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** `secret` omis = inchangé: le serveur ne l'a jamais rendu, on ne le réécrit pas. */
|
|
72
|
+
export const backupDestinationUpdate = {
|
|
73
|
+
command: 'backup.destinationUpdate' as const,
|
|
74
|
+
input: z.object({
|
|
75
|
+
destinationId,
|
|
76
|
+
name: z.string().min(1).max(BACKUP_DESTINATION_NAME_MAX),
|
|
77
|
+
deviceId: z.uuid().nullable(),
|
|
78
|
+
path: z.string().max(BACKUP_PATH_MAX),
|
|
79
|
+
endpoint: z.string().max(BACKUP_ENDPOINT_MAX).nullable(),
|
|
80
|
+
region: z.string().max(64).nullable(),
|
|
81
|
+
bucket: z.string().max(BACKUP_BUCKET_MAX).nullable(),
|
|
82
|
+
accessKeyId: z.string().max(BACKUP_ACCESS_KEY_MAX).nullable(),
|
|
83
|
+
secret: z.string().max(BACKUP_SECRET_MAX).optional(),
|
|
84
|
+
pathStyle: z.boolean()
|
|
85
|
+
}),
|
|
86
|
+
output: z.object({ destination: backupDestinationSchema })
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Retire une destination. **Refusée** tant qu'un travail la vise.
|
|
91
|
+
*
|
|
92
|
+
* Contrairement aux jetons de déploiement, qui laissent la cible orpheline mais
|
|
93
|
+
* vivante: un travail sans destination n'a aucun comportement raisonnable — il
|
|
94
|
+
* ne peut ni s'exécuter ni le dire à l'avance. Mieux vaut obliger à trancher.
|
|
95
|
+
*/
|
|
96
|
+
export const backupDestinationRemove = {
|
|
97
|
+
command: 'backup.destinationRemove' as const,
|
|
98
|
+
input: z.object({ destinationId }),
|
|
99
|
+
output: z.object({ destinationId })
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Contrôle qu'une destination est joignable et inscriptible, **maintenant**.
|
|
104
|
+
*
|
|
105
|
+
* Écrit puis relit puis efface un petit objet témoin: lister un bucket ne prouve
|
|
106
|
+
* pas qu'on peut y écrire, et découvrir le contraire à 3 h du matin est
|
|
107
|
+
* exactement ce que cette commande existe pour éviter.
|
|
108
|
+
*/
|
|
109
|
+
export const backupDestinationTest = {
|
|
110
|
+
command: 'backup.destinationTest' as const,
|
|
111
|
+
input: z.object({ destinationId }),
|
|
112
|
+
output: backupDestinationProbeSchema
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------- travaux
|
|
116
|
+
|
|
117
|
+
/** Les travaux de l'espace, avec l'état de leur dernier passage. */
|
|
118
|
+
export const backupJobList = {
|
|
119
|
+
command: 'backup.jobList' as const,
|
|
120
|
+
input: z.object({}),
|
|
121
|
+
output: z.object({ jobs: z.array(backupJobSchema) })
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Compte les travaux actifs. Métadonnée en clair pure, pour la tuile d'accueil. */
|
|
125
|
+
export const backupCount = {
|
|
126
|
+
command: 'backup.count' as const,
|
|
127
|
+
input: z.object({}),
|
|
128
|
+
output: z.object({
|
|
129
|
+
count: z.number().int().nonnegative(),
|
|
130
|
+
/** Combien ont échoué à leur dernier passage — ce que la tuile signale. */
|
|
131
|
+
failing: z.number().int().nonnegative()
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** Un travail et son historique, du plus récent au plus ancien. */
|
|
136
|
+
export const backupJobGet = {
|
|
137
|
+
command: 'backup.jobGet' as const,
|
|
138
|
+
input: z.object({ jobId, limit: z.number().int().positive().max(100).optional() }),
|
|
139
|
+
output: z.object({ job: backupJobSchema, runs: z.array(backupRunSchema) })
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const jobBody = {
|
|
143
|
+
name: z.string().min(1).max(BACKUP_JOB_NAME_MAX),
|
|
144
|
+
destinationId,
|
|
145
|
+
encryption: backupEncryptionSchema,
|
|
146
|
+
source: backupSourceKindSchema,
|
|
147
|
+
sourceId: z.number().int().positive().nullable(),
|
|
148
|
+
enabled: z.boolean(),
|
|
149
|
+
schedule: backupScheduleKindSchema,
|
|
150
|
+
scheduleHour: z.number().int().min(0).max(23),
|
|
151
|
+
scheduleWeekday: z.number().int().min(0).max(6),
|
|
152
|
+
scheduleDay: z.number().int().min(1).max(28),
|
|
153
|
+
keepLast: z.number().int().min(1).max(365)
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const backupJobAdd = {
|
|
157
|
+
command: 'backup.jobAdd' as const,
|
|
158
|
+
input: z.object(jobBody),
|
|
159
|
+
output: z.object({ job: backupJobSchema })
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const backupJobUpdate = {
|
|
163
|
+
command: 'backup.jobUpdate' as const,
|
|
164
|
+
input: z.object({ jobId, ...jobBody }),
|
|
165
|
+
output: z.object({ job: backupJobSchema })
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Supprime un travail et son historique.
|
|
170
|
+
*
|
|
171
|
+
* Les archives déjà écrites ne sont **pas** touchées: DevEye a produit des
|
|
172
|
+
* fichiers chez quelqu'un d'autre, et les effacer parce qu'on range sa
|
|
173
|
+
* configuration serait le contraire d'une sauvegarde. L'écran le dit.
|
|
174
|
+
*/
|
|
175
|
+
export const backupJobRemove = {
|
|
176
|
+
command: 'backup.jobRemove' as const,
|
|
177
|
+
input: z.object({ jobId }),
|
|
178
|
+
output: z.object({ jobId })
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Lance un travail tout de suite.
|
|
183
|
+
*
|
|
184
|
+
* Ne rend pas l'archive: une sauvegarde dure des minutes, et attendre la réponse
|
|
185
|
+
* d'une commande WS pendant ce temps-là ne marcherait pas. Elle inscrit
|
|
186
|
+
* l'exécution en `running` et rend sa ligne; l'avancement se lit en
|
|
187
|
+
* re-sollicitant, comme partout ailleurs (le sujet `backup` est diffusé à
|
|
188
|
+
* chaque transition).
|
|
189
|
+
*/
|
|
190
|
+
export const backupJobRun = {
|
|
191
|
+
command: 'backup.jobRun' as const,
|
|
192
|
+
input: z.object({ jobId }),
|
|
193
|
+
output: z.object({ run: backupRunSchema })
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** Les sources sauvegardables de l'espace, pour remplir le sélecteur. */
|
|
197
|
+
export const backupSources = {
|
|
198
|
+
command: 'backup.sources' as const,
|
|
199
|
+
input: z.object({}),
|
|
200
|
+
output: z.object({ candidates: z.array(backupSourceCandidateSchema) })
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Les dernières exécutions de l'espace, tous travaux confondus.
|
|
205
|
+
*
|
|
206
|
+
* Ce que la vue d'ensemble montre: on veut savoir « est-ce que mes sauvegardes
|
|
207
|
+
* tournent », pas ouvrir sept fiches pour le découvrir.
|
|
208
|
+
*/
|
|
209
|
+
export const backupRuns = {
|
|
210
|
+
command: 'backup.runs' as const,
|
|
211
|
+
input: z.object({ limit: z.number().int().positive().max(200).optional() }),
|
|
212
|
+
output: z.object({ runs: z.array(backupRunSchema) })
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const backupCommands = [
|
|
216
|
+
backupDestinationList,
|
|
217
|
+
backupDestinationAdd,
|
|
218
|
+
backupDestinationUpdate,
|
|
219
|
+
backupDestinationRemove,
|
|
220
|
+
backupDestinationTest,
|
|
221
|
+
backupJobList,
|
|
222
|
+
backupCount,
|
|
223
|
+
backupJobGet,
|
|
224
|
+
backupJobAdd,
|
|
225
|
+
backupJobUpdate,
|
|
226
|
+
backupJobRemove,
|
|
227
|
+
backupJobRun,
|
|
228
|
+
backupSources,
|
|
229
|
+
backupRuns
|
|
230
|
+
] as const;
|