@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,355 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Les sauvegardes de l'espace: **où** ça atterrit, **quoi** part, et **ce qui
|
|
5
|
+
* s'est passé** au dernier passage.
|
|
6
|
+
*
|
|
7
|
+
* Trois entités, et la séparation est la raison d'être du module:
|
|
8
|
+
*
|
|
9
|
+
* - une **destination** est un endroit qui accepte des octets (un dossier du
|
|
10
|
+
* serveur, un dossier d'une machine enrôlée, un bucket S3). Elle ne sait rien
|
|
11
|
+
* de ce qu'on y range;
|
|
12
|
+
* - un **travail** dit quoi sauvegarder, vers quelle destination, à quelle
|
|
13
|
+
* cadence, et combien de copies garder;
|
|
14
|
+
* - une **exécution** est ce qu'un travail a produit une fois: une archive, sa
|
|
15
|
+
* taille, son empreinte, et son sort.
|
|
16
|
+
*
|
|
17
|
+
* Croiser les deux premiers est tout l'intérêt: la même base part vers le
|
|
18
|
+
* Raspberry **et** vers un S3 distant en déclarant deux travaux, sans rien
|
|
19
|
+
* dupliquer de la configuration d'accès.
|
|
20
|
+
*
|
|
21
|
+
* ## Toujours à l'étage ouvert
|
|
22
|
+
*
|
|
23
|
+
* Comme Uptime, Déploiement et Bases de données: l'ordonnanceur tourne sans
|
|
24
|
+
* session ni mot de passe, donc tout ce qu'il doit lire — y compris la clé
|
|
25
|
+
* secrète S3 — vit sous la clé de l'espace à l'étage ouvert. Un travail qui ne
|
|
26
|
+
* pourrait s'exécuter qu'avec un humain devant l'écran ne serait pas une
|
|
27
|
+
* sauvegarde.
|
|
28
|
+
*
|
|
29
|
+
* ## Ce qui est chiffré dans l'archive, et avec quelle clé
|
|
30
|
+
*
|
|
31
|
+
* Le contenu d'une archive est scellé avec le **même format que les blobs
|
|
32
|
+
* CloudSync** (`DEVB` v2, AES-256-GCM par blocs de 1 Mio), sous une clé
|
|
33
|
+
* **dérivée de la clé serveur** (`CRYPT_KEY_A`/`CRYPT_KEY_B`) et non stockée
|
|
34
|
+
* nulle part. C'est la seule construction qui évite le serpent qui se mord la
|
|
35
|
+
* queue: une clé rangée en base serait à l'intérieur de la sauvegarde de cette
|
|
36
|
+
* base, donc illisible précisément le jour où on en a besoin.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ Corollaire à ne jamais perdre de vue: **`CRYPT_KEY_A` et `CRYPT_KEY_B` sont
|
|
39
|
+
* la sauvegarde**. Une archive chiffrée sans elles n'est qu'un fichier de bruit.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export const BACKUP_DESTINATION_NAME_MAX = 120;
|
|
43
|
+
export const BACKUP_JOB_NAME_MAX = 120;
|
|
44
|
+
export const BACKUP_PATH_MAX = 512;
|
|
45
|
+
export const BACKUP_ENDPOINT_MAX = 255;
|
|
46
|
+
export const BACKUP_BUCKET_MAX = 128;
|
|
47
|
+
export const BACKUP_ACCESS_KEY_MAX = 255;
|
|
48
|
+
export const BACKUP_SECRET_MAX = 512;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Où une archive atterrit.
|
|
52
|
+
*
|
|
53
|
+
* - `local` — un dossier du serveur DevEye, sous `BACKUP_STORAGE_DIR`. Le plus
|
|
54
|
+
* simple, et le moins protecteur: la copie meurt avec la machine qu'elle
|
|
55
|
+
* sauvegarde. Utile comme premier palier, jamais comme seul palier.
|
|
56
|
+
* - `device` — un dossier d'une machine enrôlée, écrit **par son agent**. C'est
|
|
57
|
+
* ce qui fait d'un Raspberry Pi une cible de sauvegarde sans rien y installer
|
|
58
|
+
* d'autre que l'agent qui y tourne déjà.
|
|
59
|
+
* - `s3` — un service compatible S3: Garage, MinIO, Scaleway, Backblaze,
|
|
60
|
+
* AWS. Le seul des trois qui sorte les octets du réseau local.
|
|
61
|
+
*/
|
|
62
|
+
export const backupDestinationKindSchema = z.enum(['local', 'device', 's3']);
|
|
63
|
+
export type BackupDestinationKind = z.infer<typeof backupDestinationKindSchema>;
|
|
64
|
+
|
|
65
|
+
/** Ce que le dernier contrôle d'accessibilité a dit d'une destination. */
|
|
66
|
+
export const backupDestinationStatusSchema = z.enum(['unknown', 'ok', 'error']);
|
|
67
|
+
export type BackupDestinationStatus = z.infer<typeof backupDestinationStatusSchema>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Ce qu'un travail sauvegarde.
|
|
71
|
+
*
|
|
72
|
+
* - `database` — une base supervisée de la feature Bases de données, par un
|
|
73
|
+
* vidage logique (`mysqldump` / `pg_dump`) à travers le même accès que la
|
|
74
|
+
* supervision, tunnel SSH compris.
|
|
75
|
+
* - `deveye` — la base MySQL de DevEye elle-même. **Couvre le Monitoring,
|
|
76
|
+
* l'index CloudSync, les notes, les mots de passe, tout**: ces données vivent
|
|
77
|
+
* en base, donc les sauvegarder séparément reviendrait à les copier deux
|
|
78
|
+
* fois. C'est la sauvegarde à avoir si on n'en a qu'une.
|
|
79
|
+
* - `cloudsync` — les **blobs** d'un partage CloudSync, qui sont la seule
|
|
80
|
+
* partie de DevEye à ne pas vivre en base. Rendus en clair dans une archive
|
|
81
|
+
* `tar`, arborescence d'origine reconstituée depuis l'index: une archive doit
|
|
82
|
+
* se restaurer sans DevEye, sinon ce n'est pas une sauvegarde.
|
|
83
|
+
*/
|
|
84
|
+
export const backupSourceKindSchema = z.enum(['database', 'deveye', 'cloudsync']);
|
|
85
|
+
export type BackupSourceKind = z.infer<typeof backupSourceKindSchema>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Quand un travail part.
|
|
89
|
+
*
|
|
90
|
+
* Volontairement **pas** un cron: cinq champs lisibles couvrent tout ce qu'on
|
|
91
|
+
* demande à une sauvegarde, et une expression cron mal écrite est un travail qui
|
|
92
|
+
* ne part jamais sans que rien ne le dise.
|
|
93
|
+
*/
|
|
94
|
+
export const backupScheduleKindSchema = z.enum(['manual', 'hourly', 'daily', 'weekly', 'monthly']);
|
|
95
|
+
export type BackupScheduleKind = z.infer<typeof backupScheduleKindSchema>;
|
|
96
|
+
|
|
97
|
+
/** Le sort d'une exécution. */
|
|
98
|
+
export const backupRunStatusSchema = z.enum(['running', 'success', 'failed']);
|
|
99
|
+
export type BackupRunStatus = z.infer<typeof backupRunStatusSchema>;
|
|
100
|
+
|
|
101
|
+
/** Une destination, telle que l'écran la montre. Le secret n'en sort jamais. */
|
|
102
|
+
export const backupDestinationSchema = z.object({
|
|
103
|
+
id: z.number().int().positive(),
|
|
104
|
+
kind: backupDestinationKindSchema,
|
|
105
|
+
name: z.string().max(BACKUP_DESTINATION_NAME_MAX),
|
|
106
|
+
/** `device` uniquement: la machine qui héberge le dossier. */
|
|
107
|
+
deviceId: z.uuid().nullable(),
|
|
108
|
+
/** Nom de l'appareil, joint pour l'affichage; `null` s'il a été supprimé. */
|
|
109
|
+
deviceName: z.string().nullable(),
|
|
110
|
+
/**
|
|
111
|
+
* `local`/`device`: le dossier qui reçoit les archives.
|
|
112
|
+
* `s3`: le préfixe dans le bucket (`''` = la racine).
|
|
113
|
+
*/
|
|
114
|
+
path: z.string().max(BACKUP_PATH_MAX),
|
|
115
|
+
/** `s3`: l'URL du service (`https://s3.exemple.fr`). */
|
|
116
|
+
endpoint: z.string().max(BACKUP_ENDPOINT_MAX).nullable(),
|
|
117
|
+
region: z.string().max(64).nullable(),
|
|
118
|
+
bucket: z.string().max(BACKUP_BUCKET_MAX).nullable(),
|
|
119
|
+
accessKeyId: z.string().max(BACKUP_ACCESS_KEY_MAX).nullable(),
|
|
120
|
+
/** Le secret existe-t-il ? Sa valeur n'est jamais rendue. */
|
|
121
|
+
hasSecret: z.boolean(),
|
|
122
|
+
/**
|
|
123
|
+
* Adressage par chemin (`https://hôte/bucket/clé`) plutôt que par
|
|
124
|
+
* sous-domaine. Vrai pour Garage et MinIO, faux pour AWS — et c'est la
|
|
125
|
+
* première chose qui casse quand on l'oublie.
|
|
126
|
+
*/
|
|
127
|
+
pathStyle: z.boolean(),
|
|
128
|
+
// Le chiffrement des archives n'est plus un attribut de la destination :
|
|
129
|
+
// il se règle par TRAVAIL (`backupJobSchema.encryption`, migration 094).
|
|
130
|
+
// Une destination dit où écrire, le travail dit sous quelle forme.
|
|
131
|
+
status: backupDestinationStatusSchema,
|
|
132
|
+
/** Message du dernier contrôle raté; `null` quand tout va bien. */
|
|
133
|
+
lastError: z.string().nullable(),
|
|
134
|
+
checkedAt: z.number().int().nullable(),
|
|
135
|
+
/** Combien de travaux l'utilisent — ce qu'une suppression va couper. */
|
|
136
|
+
jobCount: z.number().int().nonnegative(),
|
|
137
|
+
created: z.number().int()
|
|
138
|
+
});
|
|
139
|
+
export type BackupDestination = z.infer<typeof backupDestinationSchema>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Sous quelle forme les archives d'un travail sont écrites.
|
|
143
|
+
*
|
|
144
|
+
* - `none` : en clair. Lisible par qui tient la destination ; à réserver aux
|
|
145
|
+
* destinations déjà sous la même garde que le serveur.
|
|
146
|
+
* - `server` : scellées (AES-256-GCM) sous une clé dérivée de
|
|
147
|
+
* CRYPT_KEY_A / CRYPT_KEY_B — jamais stockée, donc jamais dans l'archive
|
|
148
|
+
* qu'elle protège, et récupérable par `scripts/restore-backup.mjs` avec ces
|
|
149
|
+
* deux seules variables.
|
|
150
|
+
*
|
|
151
|
+
* Il n'y a **pas** de mode « mot de passe » et ce n'est pas un oubli :
|
|
152
|
+
* l'ordonnanceur tourne la nuit sans session, or la clé dérivée du mot de
|
|
153
|
+
* passe ne vit que dans une session déverrouillée, en mémoire, à fenêtre
|
|
154
|
+
* glissante (voir Docs/SECURITY_MODEL.md). Un tel mode ne pourrait ni tourner
|
|
155
|
+
* planifié, ni survivre à un dump de plusieurs heures.
|
|
156
|
+
*/
|
|
157
|
+
export const backupEncryptionSchema = z.enum(['none', 'server']);
|
|
158
|
+
export type BackupEncryption = z.infer<typeof backupEncryptionSchema>;
|
|
159
|
+
|
|
160
|
+
/** Un travail: quoi, où, quand, et combien de copies on garde. */
|
|
161
|
+
export const backupJobSchema = z.object({
|
|
162
|
+
id: z.number().int().positive(),
|
|
163
|
+
name: z.string().max(BACKUP_JOB_NAME_MAX),
|
|
164
|
+
enabled: z.boolean(),
|
|
165
|
+
/** La forme des archives à venir ; chaque exécution fige la sienne. */
|
|
166
|
+
encryption: backupEncryptionSchema,
|
|
167
|
+
destinationId: z.number().int().positive(),
|
|
168
|
+
/** Recopié pour que la liste n'ait pas à recouper deux jeux de données. */
|
|
169
|
+
destinationName: z.string(),
|
|
170
|
+
destinationKind: backupDestinationKindSchema,
|
|
171
|
+
source: backupSourceKindSchema,
|
|
172
|
+
/** `database` → l'id de la connexion; `cloudsync` → l'id du partage; sinon `null`. */
|
|
173
|
+
sourceId: z.number().int().positive().nullable(),
|
|
174
|
+
/** Intitulé de la source, joint pour l'affichage; `null` si elle a disparu. */
|
|
175
|
+
sourceName: z.string().nullable(),
|
|
176
|
+
schedule: backupScheduleKindSchema,
|
|
177
|
+
/** Heure locale du serveur (0-23), pour tout sauf `hourly` et `manual`. */
|
|
178
|
+
scheduleHour: z.number().int().min(0).max(23),
|
|
179
|
+
/** Jour de la semaine, 0 = dimanche. `weekly` uniquement. */
|
|
180
|
+
scheduleWeekday: z.number().int().min(0).max(6),
|
|
181
|
+
/** Quantième, borné à 28 pour exister tous les mois. `monthly` uniquement. */
|
|
182
|
+
scheduleDay: z.number().int().min(1).max(28),
|
|
183
|
+
/** Combien d'archives réussies on garde. Au-delà, la plus ancienne part. */
|
|
184
|
+
keepLast: z.number().int().min(1).max(365),
|
|
185
|
+
nextRunAt: z.number().int().nullable(),
|
|
186
|
+
lastRunAt: z.number().int().nullable(),
|
|
187
|
+
lastStatus: backupRunStatusSchema.nullable(),
|
|
188
|
+
lastError: z.string().nullable(),
|
|
189
|
+
/** Somme des tailles des archives encore présentes. */
|
|
190
|
+
totalBytes: z.number().int().nonnegative(),
|
|
191
|
+
runCount: z.number().int().nonnegative(),
|
|
192
|
+
/**
|
|
193
|
+
* Cet élément vient d'un **autre espace**, qui le projette ici.
|
|
194
|
+
*
|
|
195
|
+
* L'écran le signale d'une pastille : sans elle, rien ne distingue une
|
|
196
|
+
* ligne locale d'une fenêtre sur l'espace voisin — et les gestes réservés
|
|
197
|
+
* au domicile (supprimer, re-partager) sembleraient cassés au lieu de
|
|
198
|
+
* s'expliquer.
|
|
199
|
+
*/
|
|
200
|
+
foreign: z.boolean(),
|
|
201
|
+
created: z.number().int()
|
|
202
|
+
});
|
|
203
|
+
export type BackupJob = z.infer<typeof backupJobSchema>;
|
|
204
|
+
|
|
205
|
+
/** Une exécution, et ce qu'elle a produit. */
|
|
206
|
+
export const backupRunSchema = z.object({
|
|
207
|
+
id: z.number().int().positive(),
|
|
208
|
+
jobId: z.number().int().positive(),
|
|
209
|
+
status: backupRunStatusSchema,
|
|
210
|
+
startedAt: z.number().int(),
|
|
211
|
+
finishedAt: z.number().int().nullable(),
|
|
212
|
+
/** Taille de l'archive écrite, en octets. `0` tant qu'elle n'est pas finie. */
|
|
213
|
+
sizeBytes: z.number().int().nonnegative(),
|
|
214
|
+
/** SHA-256 du **clair**, pour vérifier une restauration. `null` si échec. */
|
|
215
|
+
checksum: z.string().nullable(),
|
|
216
|
+
/** Chemin ou clé de l'objet écrit, tel qu'on le retrouve sur la destination. */
|
|
217
|
+
artifact: z.string().nullable(),
|
|
218
|
+
/** L'archive a-t-elle été scellée ? Recopié de la destination au moment du run. */
|
|
219
|
+
encrypted: z.boolean(),
|
|
220
|
+
/** `null` = déclenchée par l'ordonnanceur. */
|
|
221
|
+
triggeredByUserId: z.number().int().positive().nullable(),
|
|
222
|
+
error: z.string().nullable(),
|
|
223
|
+
/** L'archive existe-t-elle encore, ou la rétention l'a-t-elle effacée ? */
|
|
224
|
+
pruned: z.boolean()
|
|
225
|
+
});
|
|
226
|
+
export type BackupRun = z.infer<typeof backupRunSchema>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Une source proposée au choix, quand on crée un travail.
|
|
230
|
+
*
|
|
231
|
+
* Le client ne peut pas la construire seul: les bases vivent dans la feature
|
|
232
|
+
* Bases de données, les partages dans CloudSync, et `deveye` n'existe nulle part
|
|
233
|
+
* ailleurs qu'ici. Une commande dédiée évite trois appels croisés et trois
|
|
234
|
+
* droits à vérifier côté écran.
|
|
235
|
+
*/
|
|
236
|
+
export const backupSourceCandidateSchema = z.object({
|
|
237
|
+
kind: backupSourceKindSchema,
|
|
238
|
+
/** `null` pour `deveye`, qui est unique par nature. */
|
|
239
|
+
id: z.number().int().positive().nullable(),
|
|
240
|
+
name: z.string(),
|
|
241
|
+
/** Précision affichée en second ligne (moteur, hôte, chemin du partage). */
|
|
242
|
+
detail: z.string().nullable(),
|
|
243
|
+
/** Faux avec une raison quand la source existe mais n'est pas sauvegardable. */
|
|
244
|
+
available: z.boolean(),
|
|
245
|
+
reason: z.string().nullable()
|
|
246
|
+
});
|
|
247
|
+
export type BackupSourceCandidate = z.infer<typeof backupSourceCandidateSchema>;
|
|
248
|
+
|
|
249
|
+
/** Ce que rend un contrôle de destination. */
|
|
250
|
+
export const backupDestinationProbeSchema = z.object({
|
|
251
|
+
ok: z.boolean(),
|
|
252
|
+
error: z.string().nullable(),
|
|
253
|
+
/** Espace occupé sous le préfixe/dossier, quand la destination sait le dire. */
|
|
254
|
+
usedBytes: z.number().int().nonnegative().nullable(),
|
|
255
|
+
/** Espace libre, quand la destination sait le dire (`local` et `device`). */
|
|
256
|
+
freeBytes: z.number().int().nonnegative().nullable()
|
|
257
|
+
});
|
|
258
|
+
export type BackupDestinationProbe = z.infer<typeof backupDestinationProbeSchema>;
|
|
259
|
+
|
|
260
|
+
// ------------------------------------------------------------- lignes SQL
|
|
261
|
+
|
|
262
|
+
/** Ligne SQL (serveur uniquement). */
|
|
263
|
+
export interface BackupDestinationRow {
|
|
264
|
+
id: number;
|
|
265
|
+
workspace_id: number;
|
|
266
|
+
/** 'local' | 'device' | 's3'. */
|
|
267
|
+
kind: string;
|
|
268
|
+
device_id: string | null;
|
|
269
|
+
/** Adressage par chemin pour S3. */
|
|
270
|
+
path_style: number;
|
|
271
|
+
/** 'unknown' | 'ok' | 'error'. */
|
|
272
|
+
status: string;
|
|
273
|
+
checked_at: number | null;
|
|
274
|
+
/**
|
|
275
|
+
* { name, path, endpoint, region, bucket, accessKeyId, lastError } chiffré,
|
|
276
|
+
* étage ouvert.
|
|
277
|
+
*
|
|
278
|
+
* Le nom d'un bucket et l'adresse d'un service disent où sont les
|
|
279
|
+
* sauvegardes de quelqu'un: ce n'est pas une métadonnée de tri, ça n'a rien
|
|
280
|
+
* à faire en clair. Ne restent dehors que `kind`, `device_id` et les
|
|
281
|
+
* drapeaux — le strict nécessaire pour que l'ordonnanceur choisisse un
|
|
282
|
+
* chemin de code sans déchiffrer.
|
|
283
|
+
*/
|
|
284
|
+
content: string;
|
|
285
|
+
/** Clé secrète S3, chiffrée à l'étage ouvert. Vide pour `local`/`device`. */
|
|
286
|
+
secret_enc: string;
|
|
287
|
+
created: number;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** La même, augmentée de ce qu'une liste montre sans ouvrir la fiche. */
|
|
291
|
+
export interface BackupDestinationWithUsageRow extends BackupDestinationRow {
|
|
292
|
+
job_count: number;
|
|
293
|
+
/** `devices.name`, joint pour l'affichage. En clair en base. */
|
|
294
|
+
device_name: string | null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Ligne SQL (serveur uniquement). */
|
|
298
|
+
export interface BackupJobRow {
|
|
299
|
+
id: number;
|
|
300
|
+
workspace_id: number;
|
|
301
|
+
destination_id: number;
|
|
302
|
+
/** 'database' | 'deveye' | 'cloudsync'. */
|
|
303
|
+
source_kind: string;
|
|
304
|
+
source_id: number | null;
|
|
305
|
+
enabled: number;
|
|
306
|
+
/** 'manual' | 'hourly' | 'daily' | 'weekly' | 'monthly'. */
|
|
307
|
+
schedule_kind: string;
|
|
308
|
+
schedule_hour: number;
|
|
309
|
+
schedule_weekday: number;
|
|
310
|
+
schedule_day: number;
|
|
311
|
+
keep_last: number;
|
|
312
|
+
/** 'none' | 'server' : la forme des archives à venir. En clair, comme les
|
|
313
|
+
* colonnes d'ordonnancement : l'exécuteur choisit un chemin de code sans
|
|
314
|
+
* déchiffrer. */
|
|
315
|
+
encryption: string;
|
|
316
|
+
/**
|
|
317
|
+
* Quand l'ordonnanceur doit repasser. `NULL` = jamais (travail manuel ou
|
|
318
|
+
* désactivé), ce qui le sort de l'index des travaux dus **sans** condition
|
|
319
|
+
* supplémentaire dans la requête chaude.
|
|
320
|
+
*/
|
|
321
|
+
next_run_at: number | null;
|
|
322
|
+
/** { name } chiffré, étage ouvert. */
|
|
323
|
+
content: string;
|
|
324
|
+
created: number;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** La même, augmentée du résumé que la liste affiche. */
|
|
328
|
+
export interface BackupJobWithStateRow extends BackupJobRow {
|
|
329
|
+
destination_kind: string;
|
|
330
|
+
destination_content: string;
|
|
331
|
+
last_run_at: number | null;
|
|
332
|
+
last_status: string | null;
|
|
333
|
+
/** Le `content` chiffré de la dernière exécution ({ artifact, error }). */
|
|
334
|
+
last_run_content: string | null;
|
|
335
|
+
total_bytes: number | string | null;
|
|
336
|
+
run_count: number;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Ligne SQL (serveur uniquement). */
|
|
340
|
+
export interface BackupRunRow {
|
|
341
|
+
id: number;
|
|
342
|
+
job_id: number;
|
|
343
|
+
workspace_id: number;
|
|
344
|
+
/** 'running' | 'success' | 'failed'. */
|
|
345
|
+
status: string;
|
|
346
|
+
started_at: number;
|
|
347
|
+
finished_at: number | null;
|
|
348
|
+
size_bytes: number | string;
|
|
349
|
+
checksum: string | null;
|
|
350
|
+
encrypted: number;
|
|
351
|
+
triggered_by_user_id: number | null;
|
|
352
|
+
pruned: number;
|
|
353
|
+
/** { artifact, error } chiffré, étage ouvert. */
|
|
354
|
+
content: string;
|
|
355
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Les jetons d'accès d'un espace, tous fournisseurs confondus.
|
|
5
|
+
*
|
|
6
|
+
* **Ils appartiennent à l'espace**, pas à ce qui s'en sert : un jeton GitHub
|
|
7
|
+
* ouvre en général plusieurs dépôts, une clé Dokploy plusieurs applications, et
|
|
8
|
+
* les ressaisir par objet serait à la fois pénible et plus risqué.
|
|
9
|
+
*
|
|
10
|
+
* Une seule table, deux propriétaires. La forme d'un jeton ne dépend pas du
|
|
11
|
+
* fournisseur (une étiquette, une adresse d'instance quand le service est
|
|
12
|
+
* auto-hébergé, un secret), mais **le droit d'y toucher, si** : les jetons
|
|
13
|
+
* GitHub relèvent de la feature Git, les clés Dokploy de la feature
|
|
14
|
+
* Déploiement. Chacune n'expose que les siens, et c'est ce qui a fait sortir la
|
|
15
|
+
* clé Dokploy de l'écran des dépôts, où elle n'avait jamais eu de raison d'être
|
|
16
|
+
* — elle y avait atterri faute d'un module de déploiement pour l'accueillir.
|
|
17
|
+
*
|
|
18
|
+
* Le secret ne sort **jamais** : seule sa présence est annoncée (`hasSecret`).
|
|
19
|
+
* Même parti pris que la clé d'API météo — un secret qu'on ne renvoie pas est un
|
|
20
|
+
* secret qui ne peut fuiter ni par une capture d'écran ni par un journal.
|
|
21
|
+
*
|
|
22
|
+
* Toujours chiffré à l'**étage ouvert** : les deux services de fond qui s'en
|
|
23
|
+
* servent tournent sans session.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export const CREDENTIAL_LABEL_MAX_LENGTH = 64;
|
|
27
|
+
export const CREDENTIAL_SECRET_MAX_LENGTH = 512;
|
|
28
|
+
|
|
29
|
+
/** Les services extérieurs que DevEye sait joindre au nom de l'espace. */
|
|
30
|
+
export const credentialProviderSchema = z.enum(['github', 'dokploy']);
|
|
31
|
+
export type CredentialProvider = z.infer<typeof credentialProviderSchema>;
|
|
32
|
+
|
|
33
|
+
export const credentialSchema = z.object({
|
|
34
|
+
id: z.number().int().positive(),
|
|
35
|
+
provider: credentialProviderSchema,
|
|
36
|
+
label: z.string().max(CREDENTIAL_LABEL_MAX_LENGTH),
|
|
37
|
+
/** Racine de l'instance auto-hébergée ; `null` pour une API publique. */
|
|
38
|
+
baseUrl: z.string().nullable(),
|
|
39
|
+
hasSecret: z.boolean(),
|
|
40
|
+
created: z.number().int(),
|
|
41
|
+
/** Combien d'objets s'en servent — dépôts pour GitHub, cibles pour Dokploy. */
|
|
42
|
+
useCount: z.number().int().nonnegative()
|
|
43
|
+
});
|
|
44
|
+
export type Credential = z.infer<typeof credentialSchema>;
|
|
45
|
+
|
|
46
|
+
/** Ligne SQL (serveur uniquement). */
|
|
47
|
+
export interface CredentialRow {
|
|
48
|
+
id: number;
|
|
49
|
+
workspace_id: number;
|
|
50
|
+
provider: string;
|
|
51
|
+
label: string;
|
|
52
|
+
base_url: string | null;
|
|
53
|
+
secret_enc: string;
|
|
54
|
+
created: number;
|
|
55
|
+
}
|