@seedgrid/fe-backup 2026.7.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 (44) hide show
  1. package/README.md +86 -0
  2. package/dist/backup.d.ts +47 -0
  3. package/dist/backup.d.ts.map +1 -0
  4. package/dist/backup.js +3 -0
  5. package/dist/export-status.d.ts +6 -0
  6. package/dist/export-status.d.ts.map +1 -0
  7. package/dist/export-status.js +28 -0
  8. package/dist/index.d.ts +5 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +6 -0
  11. package/dist/messages.d.ts +308 -0
  12. package/dist/messages.d.ts.map +1 -0
  13. package/dist/messages.js +202 -0
  14. package/dist/module.d.ts +3 -0
  15. package/dist/module.d.ts.map +1 -0
  16. package/dist/module.js +23 -0
  17. package/dist/module.json +15 -0
  18. package/dist/paths.d.ts +9 -0
  19. package/dist/paths.d.ts.map +1 -0
  20. package/dist/paths.js +11 -0
  21. package/dist/server/create-backup-server.d.ts +19 -0
  22. package/dist/server/create-backup-server.d.ts.map +1 -0
  23. package/dist/server/create-backup-server.js +19 -0
  24. package/dist/server/index.d.ts +2 -0
  25. package/dist/server/index.d.ts.map +1 -0
  26. package/dist/server/index.js +2 -0
  27. package/package.json +43 -0
  28. package/scaffold/README.md +69 -0
  29. package/scaffold/src/app/api/backup/exports/[jobId]/download/route.ts +87 -0
  30. package/scaffold/src/app/backup/exports/(create)/new/page.test.tsx +182 -0
  31. package/scaffold/src/app/backup/exports/(create)/new/page.tsx +168 -0
  32. package/scaffold/src/app/backup/exports/(list)/page.test.tsx +166 -0
  33. package/scaffold/src/app/backup/exports/(list)/page.tsx +286 -0
  34. package/scaffold/src/modules/backup/components/ExportStatusBadge.test.tsx +75 -0
  35. package/scaffold/src/modules/backup/components/ExportStatusBadge.tsx +54 -0
  36. package/scaffold/src/modules/backup/config.ts +27 -0
  37. package/scaffold/src/modules/backup/index.ts +19 -0
  38. package/scaffold/src/modules/backup/services/backup-api.test.ts +122 -0
  39. package/scaffold/src/modules/backup/services/backup-api.ts +205 -0
  40. package/scaffold/src/modules/backup/services/backup-service.test.ts +125 -0
  41. package/scaffold/src/modules/backup/services/backup-service.ts +34 -0
  42. package/scaffold/src/modules/backup/types.ts +17 -0
  43. package/scaffold/src/modules/backup/useBackupAppShellSections.test.tsx +61 -0
  44. package/scaffold/src/modules/backup/useBackupAppShellSections.tsx +50 -0
@@ -0,0 +1,202 @@
1
+ // Mensagens do módulo backup/export. Promovido de
2
+ // seedgrid-cli/templates/modules/backup/src/modules/backup/messages.ts.hbs —
3
+ // era puro (zero import "@/...", zero handlebars real), mesmo critério do
4
+ // securityMessages do fe-security.
5
+ const en = {
6
+ "backup.common.module_name": "Exports",
7
+ "backup.common.loading": "Loading...",
8
+ "backup.common.actions": "Actions",
9
+ "backup.common.cancel": "Cancel",
10
+ "backup.common.confirm": "Confirm",
11
+ "backup.common.refresh": "Refresh",
12
+ "backup.common.server_connection_error": "Could not connect to the server",
13
+ "backup.exports.list.title": "Data Exports",
14
+ "backup.exports.list.description": "Create and manage tenant data export jobs.",
15
+ "backup.exports.list.new": "New export",
16
+ "backup.exports.list.empty": "No export jobs found.",
17
+ "backup.exports.list.column.job_id": "Job ID",
18
+ "backup.exports.list.column.format": "Format",
19
+ "backup.exports.list.column.scope": "Scope",
20
+ "backup.exports.list.column.status": "Status",
21
+ "backup.exports.list.column.progress": "Progress",
22
+ "backup.exports.list.column.created_at": "Created at",
23
+ "backup.exports.list.column.finished_at": "Finished at",
24
+ "backup.exports.list.column.size": "Size",
25
+ "backup.exports.list.action.download": "Download",
26
+ "backup.exports.list.action.delete": "Delete",
27
+ "backup.exports.list.action.cancel": "Cancel",
28
+ "backup.exports.list.load_error": "Could not load export jobs.",
29
+ "backup.exports.list.delete_title": "Delete export",
30
+ "backup.exports.list.delete_message": "Delete or cancel the selected export job?",
31
+ "backup.exports.list.delete_success": "Export job deleted successfully.",
32
+ "backup.exports.list.delete_error": "Could not delete the export job.",
33
+ "backup.exports.list.download_error": "Could not download the export file.",
34
+ "backup.exports.create.title": "New export",
35
+ "backup.exports.create.description": "Configure and start a new data export job.",
36
+ "backup.exports.create.format_label": "Export format",
37
+ "backup.exports.create.scope_label": "Export scope",
38
+ "backup.exports.create.attachments_label": "Include attachments",
39
+ "backup.exports.create.format_csv_zip": "CSV (Portability)",
40
+ "backup.exports.create.format_csv_zip_hint": "CSV files in ZIP. Best for migration, compliance (LGPD/GDPR), and BI tools.",
41
+ "backup.exports.create.format_sql_dump_zip": "SQL Dump (Technical Restore)",
42
+ "backup.exports.create.format_sql_dump_zip_hint": "SQL dump in ZIP. Best for disaster recovery and faithful restoration.",
43
+ "backup.exports.create.scope_domain": "Business data",
44
+ "backup.exports.create.scope_domain_hint": "Exports business entities only (recommended).",
45
+ "backup.exports.create.scope_raw": "Raw database tables",
46
+ "backup.exports.create.scope_raw_hint": "Exports all tables. For advanced users only.",
47
+ "backup.exports.create.scope_custom": "Custom selection",
48
+ "backup.exports.create.scope_custom_hint": "Select specific modules to export.",
49
+ "backup.exports.create.submit": "Start export",
50
+ "backup.exports.create.success": "Export job created successfully.",
51
+ "backup.exports.create.error": "Could not create the export job.",
52
+ "backup.exports.status.REQUESTED": "Requested",
53
+ "backup.exports.status.QUEUED": "Queued",
54
+ "backup.exports.status.RUNNING": "Running",
55
+ "backup.exports.status.DONE": "Ready",
56
+ "backup.exports.status.DONE_NOTIFIED": "Ready",
57
+ "backup.exports.status.FAILED": "Failed",
58
+ "backup.exports.status.EXPIRED": "Expired",
59
+ "backup.exports.status.CANCELLED": "Cancelled",
60
+ "backup.exports.format.CSV_ZIP": "CSV / ZIP",
61
+ "backup.exports.format.SQL_DUMP_ZIP": "SQL Dump / ZIP",
62
+ "backup.exports.scope.DOMAIN": "Business data",
63
+ "backup.exports.scope.RAW": "Raw tables",
64
+ "backup.exports.scope.CUSTOM": "Custom",
65
+ };
66
+ const ptBr = {
67
+ "backup.common.module_name": "Exportacoes",
68
+ "backup.common.loading": "Carregando...",
69
+ "backup.common.actions": "Acoes",
70
+ "backup.common.cancel": "Cancelar",
71
+ "backup.common.confirm": "Confirmar",
72
+ "backup.common.refresh": "Atualizar",
73
+ "backup.common.server_connection_error": "Erro ao conectar ao servidor",
74
+ "backup.exports.list.title": "Exportacoes de dados",
75
+ "backup.exports.list.description": "Crie e gerencie jobs de exportacao de dados do tenant.",
76
+ "backup.exports.list.new": "Nova exportacao",
77
+ "backup.exports.list.empty": "Nenhum job de exportacao encontrado.",
78
+ "backup.exports.list.column.job_id": "ID do job",
79
+ "backup.exports.list.column.format": "Formato",
80
+ "backup.exports.list.column.scope": "Escopo",
81
+ "backup.exports.list.column.status": "Status",
82
+ "backup.exports.list.column.progress": "Progresso",
83
+ "backup.exports.list.column.created_at": "Criado em",
84
+ "backup.exports.list.column.finished_at": "Finalizado em",
85
+ "backup.exports.list.column.size": "Tamanho",
86
+ "backup.exports.list.action.download": "Baixar",
87
+ "backup.exports.list.action.delete": "Excluir",
88
+ "backup.exports.list.action.cancel": "Cancelar",
89
+ "backup.exports.list.load_error": "Nao foi possivel carregar os jobs de exportacao.",
90
+ "backup.exports.list.delete_title": "Excluir exportacao",
91
+ "backup.exports.list.delete_message": "Excluir ou cancelar o job de exportacao selecionado?",
92
+ "backup.exports.list.delete_success": "Job de exportacao excluido com sucesso.",
93
+ "backup.exports.list.delete_error": "Nao foi possivel excluir o job de exportacao.",
94
+ "backup.exports.list.download_error": "Nao foi possivel baixar o arquivo de exportacao.",
95
+ "backup.exports.create.title": "Nova exportacao",
96
+ "backup.exports.create.description": "Configure e inicie um novo job de exportacao de dados.",
97
+ "backup.exports.create.format_label": "Formato de exportacao",
98
+ "backup.exports.create.scope_label": "Escopo de exportacao",
99
+ "backup.exports.create.attachments_label": "Incluir anexos",
100
+ "backup.exports.create.format_csv_zip": "CSV (Portabilidade)",
101
+ "backup.exports.create.format_csv_zip_hint": "Arquivos CSV em ZIP. Ideal para migracao, conformidade (LGPD/GDPR) e ferramentas de BI.",
102
+ "backup.exports.create.format_sql_dump_zip": "SQL Dump (Restauracao tecnica)",
103
+ "backup.exports.create.format_sql_dump_zip_hint": "Dump SQL em ZIP. Ideal para recuperacao de desastre e restauracao fiel.",
104
+ "backup.exports.create.scope_domain": "Dados de negocio",
105
+ "backup.exports.create.scope_domain_hint": "Exporta apenas entidades de negocio (recomendado).",
106
+ "backup.exports.create.scope_raw": "Tabelas brutas do banco",
107
+ "backup.exports.create.scope_raw_hint": "Exporta todas as tabelas. Somente para usuarios avancados.",
108
+ "backup.exports.create.scope_custom": "Selecao personalizada",
109
+ "backup.exports.create.scope_custom_hint": "Selecione modulos especificos para exportar.",
110
+ "backup.exports.create.submit": "Iniciar exportacao",
111
+ "backup.exports.create.success": "Job de exportacao criado com sucesso.",
112
+ "backup.exports.create.error": "Nao foi possivel criar o job de exportacao.",
113
+ "backup.exports.status.REQUESTED": "Solicitado",
114
+ "backup.exports.status.QUEUED": "Na fila",
115
+ "backup.exports.status.RUNNING": "Em andamento",
116
+ "backup.exports.status.DONE": "Pronto",
117
+ "backup.exports.status.DONE_NOTIFIED": "Pronto",
118
+ "backup.exports.status.FAILED": "Falhou",
119
+ "backup.exports.status.EXPIRED": "Expirado",
120
+ "backup.exports.status.CANCELLED": "Cancelado",
121
+ "backup.exports.format.CSV_ZIP": "CSV / ZIP",
122
+ "backup.exports.format.SQL_DUMP_ZIP": "SQL Dump / ZIP",
123
+ "backup.exports.scope.DOMAIN": "Dados de negocio",
124
+ "backup.exports.scope.RAW": "Tabelas brutas",
125
+ "backup.exports.scope.CUSTOM": "Personalizado",
126
+ };
127
+ const ptPt = {
128
+ "backup.exports.list.title": "Exportacoes de dados",
129
+ "backup.exports.list.new": "Nova exportacao",
130
+ "backup.exports.list.empty": "Nenhum job de exportacao encontrado.",
131
+ "backup.exports.list.action.download": "Transferir",
132
+ "backup.exports.create.submit": "Iniciar exportacao",
133
+ };
134
+ const es = {
135
+ "backup.common.module_name": "Exportaciones",
136
+ "backup.common.loading": "Cargando...",
137
+ "backup.common.actions": "Acciones",
138
+ "backup.common.cancel": "Cancelar",
139
+ "backup.common.confirm": "Confirmar",
140
+ "backup.common.server_connection_error": "Error al conectar con el servidor",
141
+ "backup.exports.list.title": "Exportaciones de datos",
142
+ "backup.exports.list.description": "Crea y gestiona jobs de exportacion de datos del tenant.",
143
+ "backup.exports.list.new": "Nueva exportacion",
144
+ "backup.exports.list.empty": "No se encontraron jobs de exportacion.",
145
+ "backup.exports.list.action.download": "Descargar",
146
+ "backup.exports.list.action.delete": "Eliminar",
147
+ "backup.exports.list.action.cancel": "Cancelar",
148
+ "backup.exports.list.delete_title": "Eliminar exportacion",
149
+ "backup.exports.list.delete_message": "Eliminar o cancelar el job de exportacion seleccionado?",
150
+ "backup.exports.list.delete_success": "Job de exportacion eliminado correctamente.",
151
+ "backup.exports.list.delete_error": "No se pudo eliminar el job de exportacion.",
152
+ "backup.exports.create.title": "Nueva exportacion",
153
+ "backup.exports.create.submit": "Iniciar exportacion",
154
+ "backup.exports.create.success": "Job de exportacion creado correctamente.",
155
+ "backup.exports.create.error": "No se pudo crear el job de exportacion.",
156
+ "backup.exports.status.REQUESTED": "Solicitado",
157
+ "backup.exports.status.QUEUED": "En cola",
158
+ "backup.exports.status.RUNNING": "En progreso",
159
+ "backup.exports.status.DONE": "Listo",
160
+ "backup.exports.status.DONE_NOTIFIED": "Listo",
161
+ "backup.exports.status.FAILED": "Fallido",
162
+ "backup.exports.status.EXPIRED": "Expirado",
163
+ "backup.exports.status.CANCELLED": "Cancelado",
164
+ };
165
+ const fr = {
166
+ "backup.common.module_name": "Exportations",
167
+ "backup.common.loading": "Chargement...",
168
+ "backup.common.actions": "Actions",
169
+ "backup.common.cancel": "Annuler",
170
+ "backup.common.confirm": "Confirmer",
171
+ "backup.common.server_connection_error": "Erreur de connexion au serveur",
172
+ "backup.exports.list.title": "Exportations de donnees",
173
+ "backup.exports.list.description": "Creez et gerez les jobs d exportation des donnees du tenant.",
174
+ "backup.exports.list.new": "Nouvelle exportation",
175
+ "backup.exports.list.empty": "Aucun job d exportation trouve.",
176
+ "backup.exports.list.action.download": "Telecharger",
177
+ "backup.exports.list.action.delete": "Supprimer",
178
+ "backup.exports.list.action.cancel": "Annuler",
179
+ "backup.exports.list.delete_title": "Supprimer l exportation",
180
+ "backup.exports.list.delete_message": "Supprimer ou annuler le job d exportation selectionne ?",
181
+ "backup.exports.list.delete_success": "Job d exportation supprime avec succes.",
182
+ "backup.exports.list.delete_error": "Impossible de supprimer le job d exportation.",
183
+ "backup.exports.create.title": "Nouvelle exportation",
184
+ "backup.exports.create.submit": "Demarrer l exportation",
185
+ "backup.exports.create.success": "Job d exportation cree avec succes.",
186
+ "backup.exports.create.error": "Impossible de creer le job d exportation.",
187
+ "backup.exports.status.REQUESTED": "Demande",
188
+ "backup.exports.status.QUEUED": "En attente",
189
+ "backup.exports.status.RUNNING": "En cours",
190
+ "backup.exports.status.DONE": "Pret",
191
+ "backup.exports.status.DONE_NOTIFIED": "Pret",
192
+ "backup.exports.status.FAILED": "Echoue",
193
+ "backup.exports.status.EXPIRED": "Expire",
194
+ "backup.exports.status.CANCELLED": "Annule",
195
+ };
196
+ export const backupMessages = {
197
+ en,
198
+ es: { ...en, ...es },
199
+ fr: { ...en, ...fr },
200
+ "pt-BR": { ...en, ...ptBr },
201
+ "pt-PT": { ...en, ...ptBr, ...ptPt },
202
+ };
@@ -0,0 +1,3 @@
1
+ import type { SeedGridCliModuleManifest } from "@seedgrid/fe-core";
2
+ export declare const module: SeedGridCliModuleManifest;
3
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAkBnE,eAAO,MAAM,MAAM,EAAE,yBAMpB,CAAC"}
package/dist/module.js ADDED
@@ -0,0 +1,23 @@
1
+ // Reference-adjacent implementation of the manifest read by the SeedGrid CLI
2
+ // (Rust). See seedgrid-cli/docs/adr/0004-module-manifest-contract.md.
3
+ //
4
+ // `messages` is filled in because `backupMessages` was a pure object (zero
5
+ // `@/...` import) in the old CLI template and got promoted to lib as-is —
6
+ // see scaffold/README.md.
7
+ //
8
+ // `navigation` uses the scaffold-local hook via `@/modules/backup` — the
9
+ // wiring is plain string substitution into the app-shell-navigation markers,
10
+ // so a scaffold alias is fine even though `useBackupAppShellSections`
11
+ // hardcodes a scaffold route and calls `@/modules/security`/`@/i18n`. Same
12
+ // mechanism as fe-security's `navigation`. Without it the CLI never wires the
13
+ // backup menu into the app shell.
14
+ //
15
+ // No `provider` here: unlike fe-subdomain-tenancy's `TenantProvider`, this
16
+ // module has no standalone React context to wrap the app with.
17
+ export const module = {
18
+ id: "backup",
19
+ requires: ["security"],
20
+ configKey: "backup",
21
+ navigation: { import: "useBackupAppShellSections", from: "@/modules/backup" },
22
+ messages: { import: "backupMessages", from: "@seedgrid/fe-backup" },
23
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "backup",
3
+ "requires": [
4
+ "security"
5
+ ],
6
+ "configKey": "backup",
7
+ "navigation": {
8
+ "import": "useBackupAppShellSections",
9
+ "from": "@/modules/backup"
10
+ },
11
+ "messages": {
12
+ "import": "backupMessages",
13
+ "from": "@seedgrid/fe-backup"
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ export declare const BackupPaths: {
2
+ /** POST cria export · GET lista exports. */
3
+ readonly exports: "/exports";
4
+ /** GET status · DELETE remove. */
5
+ readonly byId: (jobId: string) => string;
6
+ /** GET download (application/zip). */
7
+ readonly download: (jobId: string) => string;
8
+ };
9
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW;IACtB,4CAA4C;;IAE5C,kCAAkC;2BACpB,MAAM;IACpB,sCAAsC;+BACpB,MAAM;CAChB,CAAC"}
package/dist/paths.js ADDED
@@ -0,0 +1,11 @@
1
+ // Paths do backup/export (espelho de RestControllerPath.java em seedgrid-quarkus-ext-backup).
2
+ // A extensão monta em /exports; o prefixo final depende de como o app inclui a extensão.
3
+ const seg = (value) => encodeURIComponent(value);
4
+ export const BackupPaths = {
5
+ /** POST cria export · GET lista exports. */
6
+ exports: "/exports",
7
+ /** GET status · DELETE remove. */
8
+ byId: (jobId) => `/exports/${seg(jobId)}`,
9
+ /** GET download (application/zip). */
10
+ download: (jobId) => `/exports/${seg(jobId)}/download`,
11
+ };
@@ -0,0 +1,19 @@
1
+ import type { SecurityServer } from "@seedgrid/fe-security";
2
+ import type { ExportRequest, ExportResponse } from "../backup";
3
+ export interface BackupServer {
4
+ /** Cria um export job (POST /exports). */
5
+ createExport(req: ExportRequest): Promise<ExportResponse>;
6
+ /** Lista os export jobs (GET /exports). */
7
+ listExports(): Promise<ExportResponse[]>;
8
+ /** Status de um job — pra polling até DONE (GET /exports/{jobId}). */
9
+ getExportStatus(jobId: string): Promise<ExportResponse>;
10
+ /** Remove um job (DELETE /exports/{jobId}). */
11
+ deleteExport(jobId: string): Promise<void>;
12
+ /**
13
+ * Baixa o zip (GET /exports/{jobId}/download). Devolve o Response CRU — o route
14
+ * handler do app faz o stream pro browser (apiFetchSecurity faria .json()).
15
+ */
16
+ downloadExport(jobId: string): Promise<Response>;
17
+ }
18
+ export declare function createBackupServer(session: SecurityServer): BackupServer;
19
+ //# sourceMappingURL=create-backup-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-backup-server.d.ts","sourceRoot":"","sources":["../../src/server/create-backup-server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1D,2CAA2C;IAC3C,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACzC,sEAAsE;IACtE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxD,+CAA+C;IAC/C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAClD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,CAkBxE"}
@@ -0,0 +1,19 @@
1
+ // Helpers server-side de backup, sobre o motor de sessão do fe-security. SERVER-ONLY.
2
+ //
3
+ // const session = createSecurityServer({ ... });
4
+ // export const backup = createBackupServer(session);
5
+ import { BackupPaths } from "../paths";
6
+ export function createBackupServer(session) {
7
+ return {
8
+ createExport: (req) => session.apiFetchSecurity(BackupPaths.exports, {
9
+ method: "POST",
10
+ body: JSON.stringify(req),
11
+ }),
12
+ listExports: () => session.apiFetchSecurity(BackupPaths.exports),
13
+ getExportStatus: (jobId) => session.apiFetchSecurity(BackupPaths.byId(jobId)),
14
+ deleteExport: (jobId) => session.apiFetchSecurity(BackupPaths.byId(jobId), {
15
+ method: "DELETE",
16
+ }),
17
+ downloadExport: (jobId) => session.fetchWithSession(BackupPaths.download(jobId)),
18
+ };
19
+ }
@@ -0,0 +1,2 @@
1
+ export { createBackupServer, type BackupServer, } from "./create-backup-server";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ // Barrel SERVER (@seedgrid/fe-backup/server). Roda em action/route.
2
+ export { createBackupServer, } from "./create-backup-server";
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@seedgrid/fe-backup",
3
+ "version": "2026.7.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/SeedGrid/seedgrid-fe-components.git"
10
+ },
11
+ "homepage": "https://github.com/SeedGrid/seedgrid-fe-components",
12
+ "bugs": {
13
+ "url": "https://github.com/SeedGrid/seedgrid-fe-components/issues"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./server": {
21
+ "types": "./dist/server/index.d.ts",
22
+ "default": "./dist/server/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "scaffold"
28
+ ],
29
+ "peerDependencies": {
30
+ "@seedgrid/fe-core": "*",
31
+ "@seedgrid/fe-security": "*"
32
+ },
33
+ "devDependencies": {
34
+ "@seedgrid/fe-core": "2026.7.1",
35
+ "@seedgrid/fe-security": "2026.7.2"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "postbuild": "node scripts/emit-module-manifest.mjs",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "test": "pnpm run build && node --test tests/module-manifest.test.mjs tests/export-status.test.mjs"
42
+ }
43
+ }
@@ -0,0 +1,69 @@
1
+ # `fe-backup` scaffold
2
+
3
+ Conteúdo copiado para dentro do app gerado pelo CLI (git do app, editável à
4
+ vontade — ver `seedgrid-cli/docs/adr/0004-module-manifest-contract.md`).
5
+ Extraído de `seedgrid-cli/templates/modules/backup/` (17 arquivos, todos sem
6
+ `{{...}}` real — nenhum ficou `.hbs`).
7
+
8
+ Mesmo critério lib vs scaffold do `fe-security`/`fe-subdomain-tenancy`:
9
+ import `@/...` real = scaffold; puro ou só-default-trivial = lib.
10
+
11
+ ## Promovido pra `@seedgrid/fe-backup` (lib)
12
+
13
+ - `messages.ts` (`backupMessages`) — puro, zero import `@/...`.
14
+ - `export-status.ts` (`classifyExportStatus`, `isExportInProgress`,
15
+ `isExportDownloadable`, `ExportStatusGroup`) — puro, opera só sobre o
16
+ `ExportStatus` já em `backup.ts`.
17
+
18
+ ## Corrigido: `BackupFormat`/`ArtifactInfo` (lib) estavam errados
19
+
20
+ Conferido contra o backend real
21
+ (`seedgrid-quarkus-ext-backup/runtime/.../domains/{BackupFormat,ExportScope,ExportStatus}.java`,
22
+ `dtos/ExportResponse.java`):
23
+
24
+ - `BackupFormat` em `backup.ts` tinha 8 valores fictícios
25
+ (`CSV`, `CSV_ZIP`, `ETL`, `GDPR`, `LGPD`, `SQL`, `SQL_DUMP_ZIP`, `ZIP`) — o
26
+ enum real só tem **2**: `CSV_ZIP`, `SQL_DUMP_ZIP`. Corrigido.
27
+ - `ArtifactInfo.id` era `string` — o DTO real (`ExportResponse.ArtifactInfo.id`)
28
+ é `Long` → `number`. Corrigido.
29
+ - `ExportScope` (`DOMAIN`/`RAW`/`CUSTOM`) e `ExportStatus` (8 valores) já
30
+ batiam certinho com o backend, não precisaram de ajuste.
31
+
32
+ Antes dessa correção, o scaffold (extraído dos templates do CLI, que sempre
33
+ usaram só os 2 formatos reais) tinha sua PRÓPRIA cópia local de
34
+ `BackupFormat`/`ArtifactInfo` porque divergia da lib. Depois da correção os
35
+ dois batem, então `scaffold/src/modules/backup/types.ts` virou um simples
36
+ reexport de `@seedgrid/fe-backup` (`ArtifactInfo`, `BackupFormat`,
37
+ `ExportRequest`, `ExportResponse`, `ExportScope`, `ExportStatus`) — nada
38
+ redeclarado.
39
+
40
+ ## Ficou scaffold
41
+
42
+ - `app/backup/exports/(list)/page.tsx` (+ teste), `app/backup/exports/(create)/new/page.tsx`
43
+ (+ teste) — telas concretas.
44
+ - `app/api/backup/exports/[jobId]/download/route.ts` — route handler: por
45
+ natureza sempre scaffold (Next.js exige o arquivo num path físico
46
+ específico).
47
+ - `modules/backup/config.ts` — `backupConfig.apiBaseUrl`/`routes`/`polling`
48
+ são acoplados ao app (rotas concretas, polling interval).
49
+ - `modules/backup/services/{backup-api,backup-service}.ts` (+ testes) —
50
+ `backup-api.ts` usa `backupConfig.apiBaseUrl` (local) e `@/i18n` (1 chave
51
+ de fallback, mesmo padrão do `security-api.ts` do fe-security); `backup-service.ts`
52
+ depende de `backup-api.ts`, então segue a mesma cadeia pra scaffold.
53
+ - `modules/backup/components/ExportStatusBadge.tsx` (+ teste) — usa
54
+ `useI18n()` direto dentro do componente (chama `t(key)` internamente, ao
55
+ contrário do `StatusBadge` do fe-security que recebe `label` já traduzido
56
+ via prop). Daria pra decopiar do mesmo jeito, mas não foi feito nesta
57
+ passada — ver Follow-ups.
58
+ - `modules/backup/useBackupAppShellSections.tsx` (+ teste) — hardcoda rota
59
+ concreta (`backupConfig.routes.exports`) e usa `usePermission`/`@/i18n`.
60
+
61
+ ## Follow-ups (fora do escopo desta extração)
62
+
63
+ - `ExportStatusBadge` poderia virar lib trocando `t(key)` interno por um
64
+ prop `label: string` (mesmo padrão do `StatusBadge` do fe-security) — não
65
+ fiz por tempo, mas é uma decoupling trivial se algum dia fizer falta.
66
+ - `backup-api.ts`/`backup-service.ts` têm o mesmo tipo de acoplamento que
67
+ `security-api.ts`/`security-service.ts` do fe-security (`apiBaseUrl` local
68
+ + 1 chamada de tradução) — mesma ressalva: promover exigiria injeção de
69
+ config, redesenho deliberado, não mecânico.
@@ -0,0 +1,87 @@
1
+ // seedgrid:managed
2
+
3
+ import { type NextRequest, NextResponse } from "next/server";
4
+
5
+ import { appEnvironment } from "@/config/environment";
6
+
7
+ /**
8
+ * GET /api/backup/exports/[jobId]/download
9
+ *
10
+ * Server-side proxy that forwards the download request to the backend
11
+ * with the user's authorization token from the session cookie.
12
+ * Returns the ZIP file stream directly to the browser.
13
+ */
14
+ export async function GET(
15
+ request: NextRequest,
16
+ context: { params: Promise<{ jobId: string }> }
17
+ ) {
18
+ const { jobId } = await context.params;
19
+ const decodedJobId = decodeURIComponent(jobId);
20
+
21
+ const authHeader = request.headers.get("authorization");
22
+ const cookieHeader = request.headers.get("cookie");
23
+
24
+ const backendUrl = `${appEnvironment.hosts.apiOrigin}/exports/${encodeURIComponent(decodedJobId)}/download`;
25
+
26
+ const headers: HeadersInit = {
27
+ Accept: "application/zip, application/octet-stream",
28
+ };
29
+
30
+ if (authHeader) {
31
+ headers["Authorization"] = authHeader;
32
+ }
33
+
34
+ if (cookieHeader) {
35
+ headers["Cookie"] = cookieHeader;
36
+ }
37
+
38
+ try {
39
+ const upstream = await fetch(backendUrl, {
40
+ method: "GET",
41
+ headers,
42
+ cache: "no-store",
43
+ });
44
+
45
+ if (!upstream.ok) {
46
+ if (upstream.status === 404) {
47
+ return NextResponse.json(
48
+ { message: "Export not found or not ready" },
49
+ { status: 404 }
50
+ );
51
+ }
52
+
53
+ if (upstream.status === 410) {
54
+ return NextResponse.json(
55
+ { message: "Export expired or failed" },
56
+ { status: 410 }
57
+ );
58
+ }
59
+
60
+ return NextResponse.json(
61
+ { message: "Failed to download export" },
62
+ { status: upstream.status }
63
+ );
64
+ }
65
+
66
+ const contentDisposition =
67
+ upstream.headers.get("Content-Disposition") ??
68
+ `attachment; filename="export-${decodedJobId}.zip"`;
69
+
70
+ const contentType =
71
+ upstream.headers.get("Content-Type") ?? "application/zip";
72
+
73
+ return new NextResponse(upstream.body, {
74
+ status: 200,
75
+ headers: {
76
+ "Content-Disposition": contentDisposition,
77
+ "Content-Type": contentType,
78
+ "Cache-Control": "no-store",
79
+ },
80
+ });
81
+ } catch {
82
+ return NextResponse.json(
83
+ { message: "Could not reach the export service" },
84
+ { status: 502 }
85
+ );
86
+ }
87
+ }