@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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/package.json +68 -0
  4. package/src/domain/audience.ts +549 -0
  5. package/src/domain/backup.ts +355 -0
  6. package/src/domain/credential.ts +55 -0
  7. package/src/domain/database.ts +467 -0
  8. package/src/domain/deploy.ts +231 -0
  9. package/src/domain/device.ts +172 -0
  10. package/src/domain/deviceFiles.ts +84 -0
  11. package/src/domain/deviceLogs.ts +82 -0
  12. package/src/domain/featureRegistry.ts +392 -0
  13. package/src/domain/finance.ts +477 -0
  14. package/src/domain/git.ts +419 -0
  15. package/src/domain/home.ts +314 -0
  16. package/src/domain/live.ts +272 -0
  17. package/src/domain/logs.ts +117 -0
  18. package/src/domain/mail.ts +394 -0
  19. package/src/domain/metrics.ts +127 -0
  20. package/src/domain/note.ts +202 -0
  21. package/src/domain/notifications.ts +268 -0
  22. package/src/domain/packages.ts +35 -0
  23. package/src/domain/password.ts +36 -0
  24. package/src/domain/presence.ts +21 -0
  25. package/src/domain/project.ts +168 -0
  26. package/src/domain/projectBoard.ts +130 -0
  27. package/src/domain/projectChat.ts +46 -0
  28. package/src/domain/projectHistory.ts +82 -0
  29. package/src/domain/projectLink.ts +87 -0
  30. package/src/domain/projectPlan.ts +68 -0
  31. package/src/domain/report.ts +492 -0
  32. package/src/domain/role.ts +8 -0
  33. package/src/domain/secrecy.ts +66 -0
  34. package/src/domain/sentinel.ts +623 -0
  35. package/src/domain/sharing.ts +186 -0
  36. package/src/domain/syncProtocol.ts +116 -0
  37. package/src/domain/twoFactor.ts +40 -0
  38. package/src/domain/uptime.ts +216 -0
  39. package/src/domain/user.ts +141 -0
  40. package/src/domain/workspace.ts +56 -0
  41. package/src/domain/workspaceRole.ts +251 -0
  42. package/src/features/admin.ts +112 -0
  43. package/src/features/audience.ts +275 -0
  44. package/src/features/backup.ts +230 -0
  45. package/src/features/database.ts +461 -0
  46. package/src/features/deploy.ts +245 -0
  47. package/src/features/device.ts +292 -0
  48. package/src/features/deviceFiles.ts +83 -0
  49. package/src/features/deviceLogs.ts +36 -0
  50. package/src/features/deviceTerminal.ts +57 -0
  51. package/src/features/finance.ts +360 -0
  52. package/src/features/git.ts +368 -0
  53. package/src/features/home.ts +32 -0
  54. package/src/features/live.ts +113 -0
  55. package/src/features/logs.ts +86 -0
  56. package/src/features/mail.ts +374 -0
  57. package/src/features/metrics.ts +185 -0
  58. package/src/features/note.ts +189 -0
  59. package/src/features/notify.ts +164 -0
  60. package/src/features/password.ts +67 -0
  61. package/src/features/project.ts +709 -0
  62. package/src/features/registry.ts +103 -0
  63. package/src/features/secrecy.ts +120 -0
  64. package/src/features/sentinel.ts +233 -0
  65. package/src/features/sharing.ts +79 -0
  66. package/src/features/twoFactor.ts +47 -0
  67. package/src/features/uptime.ts +186 -0
  68. package/src/features/user.ts +91 -0
  69. package/src/features/workspace.ts +200 -0
  70. package/src/http/auth.ts +94 -0
  71. package/src/http/device.ts +222 -0
  72. package/src/http/status.ts +45 -0
  73. package/src/index.ts +1700 -0
  74. package/src/protocol/agent.ts +1171 -0
  75. package/src/protocol/envelope.ts +46 -0
  76. package/src/protocol/error.ts +27 -0
  77. package/src/protocol/result.ts +17 -0
  78. package/src/protocol/version.ts +6 -0
  79. package/src/sdk/client-ambient.d.ts +238 -0
  80. package/src/sdk/client.ts +66 -0
  81. package/src/sdk/ids.ts +25 -0
  82. package/src/sdk/index.ts +11 -0
  83. package/src/sdk/manifest.ts +326 -0
  84. package/src/sdk/providers.ts +48 -0
  85. package/src/sdk/server.ts +378 -0
  86. package/src/sdk/testing.ts +179 -0
  87. package/src/utils/version.ts +28 -0
@@ -0,0 +1,467 @@
1
+ import { z } from 'zod';
2
+ import { projectStatusSchema } from './project';
3
+
4
+ /**
5
+ * Les bases de données d'un espace.
6
+ *
7
+ * Même renversement que pour les dépôts git : une base appartient à l'espace,
8
+ * pas à un projet, et plusieurs projets peuvent pointer la même. Elle vit donc à
9
+ * l'étage **ouvert** du chiffrement — un projet confidentiel ne peut pas en
10
+ * lier, exactement comme pour un dépôt.
11
+ *
12
+ * ## Ce qui est chiffré, et ce qui ne sort jamais
13
+ *
14
+ * `content` porte l'adresse, le port, le nom de la base et l'identifiant, tous
15
+ * chiffrés. Le **mot de passe** vit à part, dans sa propre colonne, et ne quitte
16
+ * jamais le serveur : les DTO ci-dessous n'en portent qu'un `hasPassword`. Même
17
+ * règle pour le secret du tunnel. C'est la même discipline que les jetons
18
+ * d'accès git, et pour la même raison — un secret rendu au client est un secret
19
+ * qu'on ne peut plus reprendre.
20
+ *
21
+ * ## À la demande par défaut
22
+ *
23
+ * Rien ne se connecte tout seul : ouvrir la feature ne joint aucune base. Le
24
+ * relevé périodique (`monitorEnabled`) est une option, activée base par base, et
25
+ * c'est **seulement** quand elle est active que les alertes ont un sens — il
26
+ * faut bien que quelque chose les évalue.
27
+ */
28
+
29
+ export const DATABASE_NAME_MAX_LENGTH = 96;
30
+ export const DATABASE_HOST_MAX_LENGTH = 255;
31
+ export const DATABASE_USER_MAX_LENGTH = 128;
32
+ export const DATABASE_SECRET_MAX_LENGTH = 8192;
33
+ export const DATABASE_ALERT_NAME_MAX_LENGTH = 96;
34
+ export const DATABASE_ALERT_MESSAGE_MAX_LENGTH = 1000;
35
+ export const DATABASE_SQL_MAX_LENGTH = 4000;
36
+
37
+ /** Les moteurs joignables. Deux dialectes, deux adaptateurs, rien d'autre. */
38
+ export const databaseEngineSchema = z.enum(['mysql', 'postgres']);
39
+ export type DatabaseEngine = z.infer<typeof databaseEngineSchema>;
40
+
41
+ /**
42
+ * Par où passe la connexion.
43
+ *
44
+ * `direct` — le serveur joint l'hôte lui-même.
45
+ * `ssh` — un tunnel TCP est ouvert dans le processus, sans binaire externe ni
46
+ * fichier de clé sur disque.
47
+ * `socks` — la connexion transite par un proxy SOCKS5 déjà en place.
48
+ */
49
+ export const databaseAccessKindSchema = z.enum(['direct', 'ssh', 'socks']);
50
+ export type DatabaseAccessKind = z.infer<typeof databaseAccessKindSchema>;
51
+
52
+ /** Comment le tunnel s'authentifie, quand il y en a un. */
53
+ export const databaseSshAuthSchema = z.enum(['password', 'key']);
54
+ export type DatabaseSshAuth = z.infer<typeof databaseSshAuthSchema>;
55
+
56
+ /**
57
+ * L'état de la dernière connexion connue.
58
+ *
59
+ * `unknown` n'est pas une panne : c'est l'état normal d'une base qu'on n'a
60
+ * jamais jointe, ce qui est le cas par défaut de toutes. Le confondre avec
61
+ * `down` ferait passer une feature au repos pour une feature en alerte.
62
+ */
63
+ export const databaseStatusSchema = z.enum(['unknown', 'up', 'down']);
64
+ export type DatabaseStatus = z.infer<typeof databaseStatusSchema>;
65
+
66
+ /** Les réglages du tunnel, sans aucun secret. */
67
+ export const databaseAccessSchema = z.object({
68
+ kind: databaseAccessKindSchema,
69
+ /** Hôte du rebond SSH ou du proxy SOCKS ; vide en accès direct. */
70
+ host: z.string().max(DATABASE_HOST_MAX_LENGTH),
71
+ port: z.number().int().min(1).max(65535).nullable(),
72
+ /** Utilisateur SSH ; vide pour un proxy SOCKS anonyme. */
73
+ username: z.string().max(DATABASE_USER_MAX_LENGTH),
74
+ auth: databaseSshAuthSchema,
75
+ /** Un secret est enregistré (mot de passe ou clé privée) — jamais lequel. */
76
+ hasSecret: z.boolean()
77
+ });
78
+ export type DatabaseAccess = z.infer<typeof databaseAccessSchema>;
79
+
80
+ export const databaseSchema = z.object({
81
+ id: z.number().int().positive(),
82
+ engine: databaseEngineSchema,
83
+ /** Le nom que lui donne l'utilisateur ; porte l'unicité dans l'espace. */
84
+ name: z.string().max(DATABASE_NAME_MAX_LENGTH),
85
+ host: z.string().max(DATABASE_HOST_MAX_LENGTH),
86
+ port: z.number().int().min(1).max(65535),
87
+ /** Le nom de la base sur le serveur (`schema` chez PostgreSQL). */
88
+ database: z.string().max(DATABASE_NAME_MAX_LENGTH),
89
+ username: z.string().max(DATABASE_USER_MAX_LENGTH),
90
+ /** Un mot de passe est enregistré — jamais lequel. */
91
+ /**
92
+ * Cet élément vient d'un **autre espace**, qui le projette ici.
93
+ *
94
+ * L'écran le signale d'une pastille : sans elle, rien ne distingue une
95
+ * ligne locale d'une fenêtre sur l'espace voisin — et les gestes réservés
96
+ * au domicile (supprimer, re-partager) sembleraient cassés au lieu de
97
+ * s'expliquer.
98
+ */
99
+ foreign: z.boolean(),
100
+ hasPassword: z.boolean(),
101
+ access: databaseAccessSchema,
102
+ /** Le relevé périodique tourne-t-il ? Désactivé par défaut. */
103
+ monitorEnabled: z.boolean(),
104
+ /** Cadence du relevé, en secondes. Sans effet si le relevé est éteint. */
105
+ intervalSeconds: z.number().int().positive(),
106
+ /**
107
+ * Charger l'inventaire des tables dès l'ouverture de la fiche.
108
+ *
109
+ * Éteint par défaut, comme tout ce qui joint un serveur dans cette feature.
110
+ * Allumé, c'est le **seul** endroit où une connexion part sans qu'on ait
111
+ * cliqué — d'où le réglage par base plutôt qu'un comportement global.
112
+ */
113
+ autoLoadTables: z.boolean(),
114
+ lastCheckAt: z.number().int().nullable(),
115
+ /**
116
+ * Ce qu'a duré le dernier relevé, en millisecondes.
117
+ *
118
+ * Mesuré de l'ouverture de la connexion à la fin de l'inventaire : c'est le
119
+ * temps de réponse **du serveur tel qu'on l'atteint**, tunnel compris, et
120
+ * non celui d'une requête isolée. Renseigné même sur un échec — un relevé
121
+ * qui met douze secondes à échouer dit quelque chose qu'un simple
122
+ * « injoignable » ne dit pas.
123
+ */
124
+ lastElapsedMs: z.number().int().nonnegative().nullable(),
125
+ status: databaseStatusSchema,
126
+ /** Message du dernier échec, ou `null` après un succès. */
127
+ lastError: z.string().nullable(),
128
+ serverVersion: z.string().nullable(),
129
+ sizeBytes: z.number().int().nonnegative().nullable(),
130
+ tableCount: z.number().int().nonnegative().nullable(),
131
+ /** Combien d'alertes sont définies, et combien sont actuellement franchies. */
132
+ alertCount: z.number().int().nonnegative(),
133
+ firingCount: z.number().int().nonnegative(),
134
+ /** Combien de projets s'en servent — l'interconnexion, comme pour un dépôt. */
135
+ projectCount: z.number().int().nonnegative(),
136
+ created: z.number().int()
137
+ });
138
+ export type Database = z.infer<typeof databaseSchema>;
139
+
140
+ /**
141
+ * Un projet qui utilise cette base.
142
+ *
143
+ * Ne remonte que des projets à l'étage ouvert — un projet confidentiel ne peut
144
+ * pas être lié, donc le titre est toujours lisible sans session.
145
+ */
146
+ export const databaseUsageSchema = z.object({
147
+ projectId: z.number().int().positive(),
148
+ title: z.string(),
149
+ status: projectStatusSchema
150
+ });
151
+ export type DatabaseUsage = z.infer<typeof databaseUsageSchema>;
152
+
153
+ /** Le résultat d'un essai de connexion, à la demande. */
154
+ export const databaseProbeSchema = z.object({
155
+ ok: z.boolean(),
156
+ serverVersion: z.string().nullable(),
157
+ elapsedMs: z.number().int().nonnegative(),
158
+ /** Message clair, déjà traduit ; `null` en cas de succès. */
159
+ error: z.string().nullable()
160
+ });
161
+ export type DatabaseProbe = z.infer<typeof databaseProbeSchema>;
162
+
163
+ /** Une table, telle que l'exploration manuelle la montre. */
164
+ export const databaseTableSchema = z.object({
165
+ schema: z.string(),
166
+ name: z.string(),
167
+ /**
168
+ * Nombre de lignes **estimé**, tel que le moteur le tient dans ses
169
+ * statistiques : un `COUNT(*)` exact sur chaque table d'un serveur de
170
+ * production coûterait bien plus que ce que cette colonne apporte. `null`
171
+ * quand le moteur n'a pas encore analysé la table.
172
+ */
173
+ rowCount: z.number().int().nonnegative().nullable(),
174
+ sizeBytes: z.number().int().nonnegative().nullable()
175
+ });
176
+ export type DatabaseTable = z.infer<typeof databaseTableSchema>;
177
+
178
+ /**
179
+ * Le contenu d'une table, ou le résultat d'une requête.
180
+ *
181
+ * Les valeurs voyagent en **chaînes**, jamais dans leur type d'origine : un
182
+ * `BIGINT` dépasse le nombre sûr de JavaScript, une date n'a pas la même forme
183
+ * chez les deux moteurs, et un `BLOB` n'a aucune représentation JSON. Le
184
+ * formatage appartient à l'affichage ; le transport, lui, doit être fidèle.
185
+ */
186
+ export const databaseRowsSchema = z.object({
187
+ columns: z.array(z.string()),
188
+ rows: z.array(z.array(z.string().nullable())),
189
+ /** Total de lignes de la table, si le moteur a pu le donner. */
190
+ total: z.number().int().nonnegative().nullable(),
191
+ elapsedMs: z.number().int().nonnegative()
192
+ });
193
+ export type DatabaseRows = z.infer<typeof databaseRowsSchema>;
194
+
195
+ // ------------------------------------------------------------- structure
196
+
197
+ /** Une colonne, telle que le catalogue du moteur la décrit. */
198
+ export const databaseColumnSchema = z.object({
199
+ name: z.string(),
200
+ /** Le type tel que le moteur le nomme : `varchar(255)`, `int unsigned`… */
201
+ type: z.string(),
202
+ nullable: z.boolean(),
203
+ /** L'expression par défaut, telle quelle ; `null` quand il n'y en a pas. */
204
+ default: z.string().nullable(),
205
+ primaryKey: z.boolean(),
206
+ /**
207
+ * Le moteur la remplit seul : auto-incrément, identité, colonne générée.
208
+ * Le formulaire d'ajout ne la propose donc pas — la renseigner à la main
209
+ * serait au mieux ignoré, au pire refusé.
210
+ */
211
+ generated: z.boolean(),
212
+ comment: z.string()
213
+ });
214
+ export type DatabaseColumn = z.infer<typeof databaseColumnSchema>;
215
+
216
+ /**
217
+ * Une clé étrangère, et ce qu'elle vise.
218
+ *
219
+ * `columns` et `refColumns` sont **appariées par position** : la première de
220
+ * l'une pointe la première de l'autre. C'est ce qui permet de suivre une
221
+ * contrainte composite sans deviner.
222
+ */
223
+ export const databaseForeignKeySchema = z.object({
224
+ name: z.string(),
225
+ columns: z.array(z.string()).min(1),
226
+ refSchema: z.string(),
227
+ refTable: z.string(),
228
+ refColumns: z.array(z.string()).min(1)
229
+ });
230
+ export type DatabaseForeignKey = z.infer<typeof databaseForeignKeySchema>;
231
+
232
+ /** Un index, clé primaire exclue — celle-ci est portée par les colonnes. */
233
+ export const databaseIndexSchema = z.object({
234
+ name: z.string(),
235
+ columns: z.array(z.string()),
236
+ unique: z.boolean()
237
+ });
238
+ export type DatabaseIndex = z.infer<typeof databaseIndexSchema>;
239
+
240
+ /**
241
+ * La structure d'une table : ce qu'il faut pour la lire, l'écrire et la suivre.
242
+ *
243
+ * Un seul objet parce que ses trois usages sont indissociables : le panneau
244
+ * « Structure » l'affiche, le formulaire de ligne en tire ses champs, et la
245
+ * navigation par clé étrangère en tire ses liens. Les charger séparément
246
+ * multiplierait les allers-retours pour une même sélection de table.
247
+ */
248
+ export const databaseStructureSchema = z.object({
249
+ schema: z.string(),
250
+ table: z.string(),
251
+ columns: z.array(databaseColumnSchema),
252
+ /**
253
+ * Les colonnes qui désignent une ligne, dans l'ordre de la clé.
254
+ *
255
+ * **Vide = pas de clé primaire**, et c'est décisif : sans elle, aucune
256
+ * modification ni suppression n'est proposée. Une table sans clé ne permet
257
+ * pas de nommer *une* ligne, et un `DELETE` qui en emporterait deux est un
258
+ * accident qu'on ne peut pas rattraper.
259
+ */
260
+ primaryKey: z.array(z.string()),
261
+ foreignKeys: z.array(databaseForeignKeySchema),
262
+ indexes: z.array(databaseIndexSchema)
263
+ });
264
+ export type DatabaseStructure = z.infer<typeof databaseStructureSchema>;
265
+
266
+ // --------------------------------------------------------------- recherche
267
+
268
+ /**
269
+ * Comment une colonne est confrontée à une valeur.
270
+ *
271
+ * Fermé, et c'est le point : la recherche ne transporte jamais de fragment de
272
+ * SQL. Le serveur choisit l'opérateur dans cette liste et lie la valeur en
273
+ * paramètre — une valeur ne peut donc pas devenir du code.
274
+ */
275
+ export const databaseFilterOperatorSchema = z.enum([
276
+ 'eq',
277
+ 'ne',
278
+ 'contains',
279
+ 'starts',
280
+ 'ends',
281
+ 'gt',
282
+ 'gte',
283
+ 'lt',
284
+ 'lte',
285
+ 'isNull',
286
+ 'notNull'
287
+ ]);
288
+ export type DatabaseFilterOperator = z.infer<typeof databaseFilterOperatorSchema>;
289
+
290
+ /** Un critère de recherche. `value` est ignorée par `isNull` / `notNull`. */
291
+ export const databaseFilterSchema = z.object({
292
+ column: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
293
+ operator: databaseFilterOperatorSchema,
294
+ value: z.string().max(1000)
295
+ });
296
+ export type DatabaseFilter = z.infer<typeof databaseFilterSchema>;
297
+
298
+ /** L'ordre d'affichage demandé, colonne validée contre la table réelle. */
299
+ export const databaseSortSchema = z.object({
300
+ column: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
301
+ direction: z.enum(['asc', 'desc'])
302
+ });
303
+ export type DatabaseSort = z.infer<typeof databaseSortSchema>;
304
+
305
+ // ------------------------------------------------------- écriture de lignes
306
+
307
+ /**
308
+ * La valeur d'une colonne, à l'écriture.
309
+ *
310
+ * `null` est un vrai `NULL`, et non la chaîne vide : les deux se distinguent à
311
+ * la saisie, et les confondre viderait une colonne « non nulle » au lieu de
312
+ * refuser. Tout le reste voyage en chaîne, comme à la lecture — le moteur
313
+ * convertit, le paramètre étant lié.
314
+ */
315
+ export const databaseCellSchema = z.object({
316
+ column: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
317
+ value: z.string().max(65535).nullable()
318
+ });
319
+ export type DatabaseCell = z.infer<typeof databaseCellSchema>;
320
+
321
+ /** Ce qu'une instruction libre a produit : des lignes, ou un décompte. */
322
+ export const databaseExecutionSchema = z.object({
323
+ /** Le résultat d'une lecture ; `null` pour une écriture. */
324
+ rows: databaseRowsSchema.nullable(),
325
+ /** Le nombre de lignes touchées par une écriture ; `null` pour une lecture. */
326
+ affected: z.number().int().nonnegative().nullable(),
327
+ elapsedMs: z.number().int().nonnegative()
328
+ });
329
+ export type DatabaseExecution = z.infer<typeof databaseExecutionSchema>;
330
+
331
+ /** Les formats d'export proposés. */
332
+ export const databaseExportFormatSchema = z.enum(['csv', 'json', 'sql']);
333
+ export type DatabaseExportFormat = z.infer<typeof databaseExportFormatSchema>;
334
+
335
+ /**
336
+ * Une plage d'identifiants à exporter, bornes comprises.
337
+ *
338
+ * Deux nombres et non un fragment de texte : « 1-500, 900 » est une commodité de
339
+ * saisie, elle est analysée dans le navigateur et ne traverse jamais le contrat.
340
+ * Le serveur ne reçoit donc que des bornes, qu'il lie en paramètres — un
341
+ * identifiant saisi ne peut pas devenir du SQL. Une valeur seule s'écrit
342
+ * `{ from: n, to: n }`.
343
+ */
344
+ export const databaseIdRangeSchema = z.object({
345
+ from: z.number().int(),
346
+ to: z.number().int()
347
+ });
348
+ export type DatabaseIdRange = z.infer<typeof databaseIdRangeSchema>;
349
+
350
+ /** Comment une mesure est comparée à son seuil. */
351
+ export const databaseComparatorSchema = z.enum(['gt', 'gte', 'lt', 'lte', 'eq', 'ne']);
352
+ export type DatabaseComparator = z.infer<typeof databaseComparatorSchema>;
353
+
354
+ /**
355
+ * Une condition d'alerte : une requête qui rend **un seul nombre**, comparée à
356
+ * un seuil.
357
+ *
358
+ * Un seul nombre, et c'est la contrainte qui rend le reste possible : un
359
+ * `SELECT COUNT(*) …` ou un `SELECT AVG(…) …` se compare, se raconte dans un
360
+ * message et se relit dans l'historique. Une requête qui rendrait un tableau
361
+ * n'aurait pas de vérité à confronter à un seuil.
362
+ */
363
+ export const databaseConditionSchema = z.object({
364
+ sql: z.string().min(1).max(DATABASE_SQL_MAX_LENGTH),
365
+ comparator: databaseComparatorSchema,
366
+ threshold: z.number(),
367
+ /** Nom court de la mesure, repris dans le message ({@link databaseAlertSchema}). */
368
+ label: z.string().max(DATABASE_ALERT_NAME_MAX_LENGTH)
369
+ });
370
+ export type DatabaseCondition = z.infer<typeof databaseConditionSchema>;
371
+
372
+ /** Comment les conditions se combinent. */
373
+ export const databaseCombinatorSchema = z.enum(['and', 'or']);
374
+ export type DatabaseCombinator = z.infer<typeof databaseCombinatorSchema>;
375
+
376
+ /**
377
+ * Une alerte : des conditions, un opérateur qui les relie, un message.
378
+ *
379
+ * Évaluée par le relevé périodique, donc **seulement si celui-ci est actif** sur
380
+ * la base. Une alerte définie sur une base au repos est inerte, et l'interface
381
+ * le dit plutôt que de laisser croire à une surveillance qui n'existe pas.
382
+ *
383
+ * La notification part sur les canaux de l'espace — le compte mail et le webhook
384
+ * réglés dans Uptime — parce que ce sont les mêmes canaux pour les mêmes
385
+ * personnes, et qu'en avoir deux jeux à tenir à jour serait une source d'erreur
386
+ * de plus.
387
+ */
388
+ export const databaseAlertSchema = z.object({
389
+ id: z.number().int().positive(),
390
+ databaseId: z.number().int().positive(),
391
+ name: z.string().max(DATABASE_ALERT_NAME_MAX_LENGTH),
392
+ enabled: z.boolean(),
393
+ combinator: databaseCombinatorSchema,
394
+ conditions: z.array(databaseConditionSchema),
395
+ /**
396
+ * Le message envoyé. `{label}` y est remplacé par la valeur mesurée de la
397
+ * condition portant ce nom — c'est ce qui permet d'écrire « déjà {erreurs}
398
+ * erreurs cette heure-ci » plutôt qu'un texte qui ne dit rien de la mesure.
399
+ */
400
+ message: z.string().max(DATABASE_ALERT_MESSAGE_MAX_LENGTH),
401
+ /** L'alerte est-elle franchie **en ce moment** ? */
402
+ firing: z.boolean(),
403
+ /** Dernière évaluation, et dernier déclenchement (deux dates distinctes). */
404
+ lastCheckAt: z.number().int().nullable(),
405
+ lastFiredAt: z.number().int().nullable(),
406
+ /** Ce qu'a rendu la dernière évaluation, condition par condition. */
407
+ lastValues: z.array(z.number().nullable()),
408
+ /** Pourquoi la dernière évaluation a échoué, s'il y a lieu. */
409
+ lastError: z.string().nullable(),
410
+ created: z.number().int()
411
+ });
412
+ export type DatabaseAlert = z.infer<typeof databaseAlertSchema>;
413
+
414
+ // --------------------------------------------------------------- lignes SQL
415
+
416
+ /** Ligne SQL (serveur uniquement). */
417
+ export interface DatabaseRow {
418
+ id: number;
419
+ workspace_id: number;
420
+ engine: string;
421
+ /** Condensé du nom en minuscules : porte l'unicité dans l'espace. */
422
+ name_ref: string;
423
+ sort_order: number;
424
+ monitor_enabled: number;
425
+ interval_seconds: number;
426
+ last_check_at: number | null;
427
+ /** Durée du dernier relevé, en ms. Renseignée aussi sur un échec. */
428
+ last_elapsed_ms: number | null;
429
+ status: string;
430
+ last_error: string | null;
431
+ server_version: string | null;
432
+ size_bytes: number | null;
433
+ table_count: number | null;
434
+ /** { name, host, port, database, username } chiffré. */
435
+ content: string;
436
+ /** Mot de passe de la base, chiffré. Ne sort jamais du serveur. */
437
+ secret_enc: string | null;
438
+ /** { kind, host, port, username, auth } chiffré. */
439
+ access_content: string | null;
440
+ /** Mot de passe SSH ou clé privée, chiffré. Ne sort jamais du serveur. */
441
+ access_secret_enc: string | null;
442
+ created: number;
443
+ }
444
+
445
+ /** Ligne SQL (serveur uniquement). */
446
+ export interface DatabaseAlertRow {
447
+ id: number;
448
+ database_id: number;
449
+ workspace_id: number;
450
+ enabled: number;
451
+ combinator: string;
452
+ firing: number;
453
+ last_check_at: number | null;
454
+ last_fired_at: number | null;
455
+ last_error: string | null;
456
+ /** { name, conditions, message, lastValues } chiffré. */
457
+ content: string;
458
+ created: number;
459
+ }
460
+
461
+ /** Ligne SQL (serveur uniquement) : la liaison projet → base. */
462
+ export interface ProjectDatabaseLinkRow {
463
+ project_id: number;
464
+ database_id: number;
465
+ workspace_id: number;
466
+ created: number;
467
+ }
@@ -0,0 +1,231 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Les cibles de déploiement d'un espace, et l'historique de ce qu'on y a poussé.
5
+ *
6
+ * **Une cible appartient à l'espace, pas à un projet.** C'est le renversement
7
+ * qui fonde cette feature, le même que celui des dépôts git : une pile compose
8
+ * sert souvent deux projets (un client, un serveur), et la modéliser comme une
9
+ * propriété de l'un d'eux interdisait à l'autre de la voir. Un projet n'en garde
10
+ * donc qu'une **liaison** (`project_deploy_links`), et délier n'efface jamais la
11
+ * cible.
12
+ *
13
+ * Portée volontairement **étroite** : déclencher et suivre. DevEye ne configure
14
+ * pas le déploiement, ne gère ni domaines ni variables d'environnement — tout
15
+ * cela vit chez le fournisseur, qui le fait mieux. Ce module répond à deux
16
+ * questions : « est-ce que je peux lancer ça d'ici ? » et « où en est le
17
+ * dernier ? ».
18
+ *
19
+ * **Toujours à l'étage ouvert**, quel que soit le tier des projets qui s'y
20
+ * rattachent : le suivi d'état tourne sans session, et une cible d'espace ne
21
+ * peut pas suivre le palier de confidentialité de l'un de ses projets.
22
+ * Corollaire assumé, identique à celui des dépôts : un projet confidentiel n'a
23
+ * pas de déploiement.
24
+ */
25
+
26
+ export const DEPLOY_TITLE_MAX_LENGTH = 120;
27
+ export const DEPLOY_DESCRIPTION_MAX_LENGTH = 500;
28
+ export const DEPLOY_TARGET_NAME_MAX_LENGTH = 120;
29
+ export const DEPLOY_EXTERNAL_ID_MAX_LENGTH = 128;
30
+
31
+ /**
32
+ * Les fournisseurs que le module sait déclencher.
33
+ *
34
+ * Un seul pour l'instant, et l'énumération est là pour que le second n'ait pas à
35
+ * réécrire le contrat. Distinct de `credentialProviderSchema`, qui couvre aussi
36
+ * GitHub : un jeton GitHub ne déploie rien.
37
+ */
38
+ export const deployProviderSchema = z.enum(['dokploy']);
39
+ export type DeployProvider = z.infer<typeof deployProviderSchema>;
40
+
41
+ /**
42
+ * L'état d'un déploiement, ramené à quatre valeurs.
43
+ *
44
+ * Les fournisseurs ont chacun leur vocabulaire (`done`, `success`, `idle`,
45
+ * `error`, `failed`…). L'adaptateur les projette là-dessus, pour que
46
+ * l'interface n'ait qu'un seul jeu d'états à connaître.
47
+ */
48
+ export const deployStatusSchema = z.enum(['queued', 'running', 'success', 'failed']);
49
+ export type DeployStatus = z.infer<typeof deployStatusSchema>;
50
+
51
+ /**
52
+ * Ce qu'on déploie chez Dokploy : une **application** ou une pile **compose**.
53
+ *
54
+ * La distinction n'est pas cosmétique — chaque type a sa propre procédure de
55
+ * déclenchement (`application.deploy` / `compose.deploy`) et sa propre
56
+ * procédure d'historique (`deployment.all` / `deployment.allByCompose`). Une
57
+ * cible sans son type serait indéployable.
58
+ *
59
+ * En pratique, une infra Dokploy est souvent majoritairement composée de piles
60
+ * compose : les ignorer reviendrait à ne rien pouvoir déployer.
61
+ */
62
+ export const deployTargetKindSchema = z.enum(['application', 'compose']);
63
+ export type DeployTargetKind = z.infer<typeof deployTargetKindSchema>;
64
+
65
+ /** Une cible de l'espace, et l'état de son dernier déclenchement. */
66
+ export const deployTargetSchema = z.object({
67
+ id: z.number().int().positive(),
68
+ provider: deployProviderSchema,
69
+ kind: deployTargetKindSchema,
70
+ /** Identifiant de la cible chez le fournisseur. En clair : il porte l'unicité. */
71
+ externalId: z.string().max(DEPLOY_EXTERNAL_ID_MAX_LENGTH),
72
+ name: z.string().max(DEPLOY_TARGET_NAME_MAX_LENGTH),
73
+ /** `null` = le jeton a été retiré ; la cible reste, indéployable, et le dit. */
74
+ credentialId: z.number().int().positive().nullable(),
75
+ /**
76
+ * L'adresse de l'instance qui l'héberge, recopiée du jeton.
77
+ *
78
+ * Deux instances Dokploy peuvent servir la même pile sous le même nom ; sans
79
+ * elle, deux lignes de la liste seraient indiscernables. Elle vient du jeton
80
+ * et n'est jamais saisie ici.
81
+ */
82
+ baseUrl: z.string().nullable(),
83
+ /** L'état du dernier déploiement, ou `null` si rien n'est jamais parti d'ici. */
84
+ lastStatus: deployStatusSchema.nullable(),
85
+ lastDeployAt: z.number().int().nullable(),
86
+ /**
87
+ * Cet élément vient d'un **autre espace**, qui le projette ici.
88
+ *
89
+ * L'écran le signale d'une pastille : sans elle, rien ne distingue une
90
+ * ligne locale d'une fenêtre sur l'espace voisin — et les gestes réservés
91
+ * au domicile (supprimer, re-partager) sembleraient cassés au lieu de
92
+ * s'expliquer.
93
+ */
94
+ foreign: z.boolean(),
95
+ /** Combien de projets la déploient. */
96
+ projectCount: z.number().int().nonnegative(),
97
+ created: z.number().int()
98
+ });
99
+ export type DeployTarget = z.infer<typeof deployTargetSchema>;
100
+
101
+ /** Une cible proposée au choix, telle que le fournisseur la déclare. */
102
+ export const deployCandidateSchema = z.object({
103
+ kind: deployTargetKindSchema,
104
+ externalId: z.string(),
105
+ name: z.string(),
106
+ /** Chemin lisible chez le fournisseur (projet / environnement), s'il en donne un. */
107
+ path: z.string().nullable()
108
+ });
109
+ export type DeployCandidate = z.infer<typeof deployCandidateSchema>;
110
+
111
+ /** Un déclenchement, et ce qu'il est devenu. */
112
+ export const deploymentSchema = z.object({
113
+ id: z.number().int().positive(),
114
+ targetId: z.number().int().positive(),
115
+ /** Identifiant chez le fournisseur, quand il en donne un au déclenchement. */
116
+ externalId: z.string().nullable(),
117
+ status: deployStatusSchema,
118
+ /** Qui l'a déclenché ; `null` = tâche de fond, ou compte supprimé depuis. */
119
+ triggeredByUserId: z.number().int().positive().nullable(),
120
+ title: z.string(),
121
+ description: z.string(),
122
+ url: z.string().nullable(),
123
+ startedAt: z.number().int(),
124
+ finishedAt: z.number().int().nullable()
125
+ });
126
+ export type Deployment = z.infer<typeof deploymentSchema>;
127
+
128
+ /**
129
+ * Une ligne d'historique telle que le fournisseur la connaît — pas seulement
130
+ * ce que DevEye a déclenché.
131
+ *
132
+ * `deploymentSchema` porte l'identité DevEye d'un déclenchement (`id`, `targetId`,
133
+ * `triggeredByUserId`) ; celui-ci n'a que ce que Dokploy rend, y compris pour ce
134
+ * qui est parti de sa propre interface ou d'une CI. Aucun `id` DevEye n'existe
135
+ * pour ces lignes-là, d'où un schéma distinct plutôt qu'un `Deployment` aux
136
+ * champs devinés.
137
+ */
138
+ export const deployHistoryEntrySchema = z.object({
139
+ externalId: z.string().nullable(),
140
+ status: deployStatusSchema,
141
+ title: z.string(),
142
+ description: z.string(),
143
+ startedAt: z.number().int(),
144
+ finishedAt: z.number().int().nullable()
145
+ });
146
+ export type DeployHistoryEntry = z.infer<typeof deployHistoryEntrySchema>;
147
+
148
+ /** Ligne SQL (serveur uniquement). */
149
+ export interface DeployTargetRow {
150
+ id: number;
151
+ workspace_id: number;
152
+ credential_id: number | null;
153
+ provider: string;
154
+ /** 'application' | 'compose'. */
155
+ target_kind: string;
156
+ external_id: string;
157
+ /** Rang dans la liste, entièrement défini par l'utilisateur (`deploy.reorder`). */
158
+ sort_order: number;
159
+ content: string;
160
+ /**
161
+ * Dernier rapprochement réussi avec le fournisseur ; `null` = jamais.
162
+ *
163
+ * Porte deux rôles à la fois, et c'est voulu : il ordonne les cibles à
164
+ * réinterroger (la plus ancienne d'abord), **et** il distingue le premier
165
+ * rapprochement des suivants. Cette seconde lecture est ce qui empêche
166
+ * l'import initial de notifier : la première fois, tout l'historique de la
167
+ * cible est « nouveau » sans que rien ne vienne de se produire.
168
+ */
169
+ synced_at: number | null;
170
+ created: number;
171
+ }
172
+
173
+ /**
174
+ * La même, augmentée de ce qu'une liste montre sans ouvrir la fiche : combien de
175
+ * projets s'en servent, et où en est le dernier déploiement. Calculé par
176
+ * jointure plutôt que recopié dans des colonnes, qui dériveraient.
177
+ */
178
+ export interface DeployTargetWithUsageRow extends DeployTargetRow {
179
+ base_url: string | null;
180
+ project_count: number;
181
+ last_status: string | null;
182
+ last_deploy_at: number | null;
183
+ }
184
+
185
+ /** Ligne SQL (serveur uniquement). */
186
+ export interface DeploymentRow {
187
+ id: number;
188
+ target_id: number;
189
+ workspace_id: number;
190
+ external_id: string | null;
191
+ status: string;
192
+ triggered_by_user_id: number | null;
193
+ started_at: number;
194
+ finished_at: number | null;
195
+ /**
196
+ * Un avis est-il déjà parti pour ce déploiement ?
197
+ *
198
+ * Même rôle que `uptime_incidents.notified`, et pour la même raison : un avis
199
+ * appartient au **déploiement**, pas au tour de sondage qui l'a vu. Sans
200
+ * cette colonne, chaque tour renotifierait le même échec — et l'import
201
+ * initial d'une cible en enverrait un par ligne d'historique.
202
+ */
203
+ notified: number;
204
+ content: string;
205
+ }
206
+
207
+ /**
208
+ * Une cible à réinterroger, avec l'adresse de son instance et de quoi choisir
209
+ * son tour.
210
+ *
211
+ * `base_url` vient du jeton par jointure : la boucle de fond n'a pas de session
212
+ * pour repasser par la feature, et faire un second aller-retour par cible pour
213
+ * lire son jeton coûterait une requête de plus pour une donnée déjà jointe.
214
+ *
215
+ * `in_flight` compte les déploiements non terminés que DevEye connaît. Il ne
216
+ * sert qu'à trier : une cible qui a quelque chose en vol passe à chaque tour,
217
+ * les autres attendent leur cadence. C'est ce qui permet de suivre un
218
+ * déploiement à la minute sans sonder toutes les cibles aussi souvent.
219
+ */
220
+ export interface DeployTargetSyncRow extends DeployTargetRow {
221
+ base_url: string | null;
222
+ in_flight: number;
223
+ }
224
+
225
+ /** Ligne SQL (serveur uniquement) : la liaison projet → cible. */
226
+ export interface ProjectDeployLinkRow {
227
+ project_id: number;
228
+ target_id: number;
229
+ workspace_id: number;
230
+ created: number;
231
+ }