@v1ct0rbr/minivault-web 0.4.3 → 0.5.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/README.md CHANGED
@@ -1,75 +1,75 @@
1
- # minivault-web
2
-
3
- Módulo independente de **gestão de backups** (cliente do serviço
4
- [minivault](../minivault)). Consumível como pacote npm em qualquer app React,
5
- estilizado com Bootstrap. Comunica-se com a API REST do minivault — normalmente
6
- através de um **proxy autenticado** (ex.: `/minivault` no backend do Siga+), de
7
- modo que a `X-API-Key` do minivault jamais trafega no browser.
8
-
9
- ## Recursos
10
-
11
- - Lista de backups com paginação, status, tamanho e data.
12
- - Criação de backup (com origem de arquivos LOCAL ou S3, opcional).
13
- - Restauração de backup em um banco informado.
14
- - Importação de dump SQL em texto (`psql`/`mysql`) via upload.
15
- - Download e exclusão de backups.
16
- - Tema claro/escuro e auto-refresh.
17
-
18
- ## Instalação
19
-
20
- ```bash
21
- npm install @v1ct0rbr/minivault-web
22
- ```
23
-
24
- Como dependência local durante o desenvolvimento:
25
-
26
- ```bash
27
- npm install file:../minivault-web
28
- ```
29
-
30
- ## Uso no Siga+ (proxy autenticado)
31
-
32
- ```tsx
33
- import { MinivaultModule } from '@v1ct0rbr/minivault-web'
34
- import '@v1ct0rbr/minivault-web/style.css'
35
- import { useAuthStore } from '@/store/useAuthStore'
36
-
37
- const apiBase = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080'
38
-
39
- export function MinivaultPage() {
40
- const getToken = () => useAuthStore.getState().token
41
- return <MinivaultModule baseUrl={apiBase + '/minivault'} getToken={getToken} />
42
- }
43
- ```
44
-
45
- ## Uso standalone (desenvolvimento)
46
-
47
- ```tsx
48
- <MinivaultModule baseUrl="http://localhost:8081" token="api-key-do-minivault" />
49
- ```
50
-
51
- > Em produção o token do minivault deve ficar apenas no proxy do backend, nunca
52
- > no bundle do frontend.
53
-
54
- ## Desenvolvimento
55
-
56
- ```bash
57
- npm install
58
- npm run dev # playground standalone (porta 5176)
59
- npm run build:lib # gera dist/ (lib ES + types)
60
- npm test # vitest
61
- npm run typecheck # tsc --noEmit
62
- ```
63
-
64
- ### Playground standalone
65
-
66
- ```bash
67
- VITE_MINIVAULT_API_URL=http://localhost:8081 VITE_MINIVAULT_API_TOKEN=chave npm run dev
68
- ```
69
-
70
- ## Publicação
71
-
72
- ```bash
73
- npm run build:lib && npm test
74
- npm publish --access public
1
+ # minivault-web
2
+
3
+ Módulo independente de **gestão de backups** (cliente do serviço
4
+ [minivault](../minivault)). Consumível como pacote npm em qualquer app React,
5
+ estilizado com Bootstrap. Comunica-se com a API REST do minivault — normalmente
6
+ através de um **proxy autenticado** (ex.: `/minivault` no backend do Siga+), de
7
+ modo que a `X-API-Key` do minivault jamais trafega no browser.
8
+
9
+ ## Recursos
10
+
11
+ - Lista de backups com paginação, status, tamanho e data.
12
+ - Criação de backup (com origem de arquivos LOCAL ou S3, opcional).
13
+ - Restauração de backup em um banco informado.
14
+ - Importação de dump SQL em texto (`psql`/`mysql`) via upload.
15
+ - Download e exclusão de backups.
16
+ - Tema claro/escuro e auto-refresh.
17
+
18
+ ## Instalação
19
+
20
+ ```bash
21
+ npm install @v1ct0rbr/minivault-web
22
+ ```
23
+
24
+ Como dependência local durante o desenvolvimento:
25
+
26
+ ```bash
27
+ npm install file:../minivault-web
28
+ ```
29
+
30
+ ## Uso no Siga+ (proxy autenticado)
31
+
32
+ ```tsx
33
+ import { MinivaultModule } from '@v1ct0rbr/minivault-web'
34
+ import '@v1ct0rbr/minivault-web/style.css'
35
+ import { useAuthStore } from '@/store/useAuthStore'
36
+
37
+ const apiBase = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080'
38
+
39
+ export function MinivaultPage() {
40
+ const getToken = () => useAuthStore.getState().token
41
+ return <MinivaultModule baseUrl={apiBase + '/minivault'} getToken={getToken} />
42
+ }
43
+ ```
44
+
45
+ ## Uso standalone (desenvolvimento)
46
+
47
+ ```tsx
48
+ <MinivaultModule baseUrl="http://localhost:8081" token="api-key-do-minivault" />
49
+ ```
50
+
51
+ > Em produção o token do minivault deve ficar apenas no proxy do backend, nunca
52
+ > no bundle do frontend.
53
+
54
+ ## Desenvolvimento
55
+
56
+ ```bash
57
+ npm install
58
+ npm run dev # playground standalone (porta 5176)
59
+ npm run build:lib # gera dist/ (lib ES + types)
60
+ npm test # vitest
61
+ npm run typecheck # tsc --noEmit
62
+ ```
63
+
64
+ ### Playground standalone
65
+
66
+ ```bash
67
+ VITE_MINIVAULT_API_URL=http://localhost:8081 VITE_MINIVAULT_API_TOKEN=chave npm run dev
68
+ ```
69
+
70
+ ## Publicação
71
+
72
+ ```bash
73
+ npm run build:lib && npm test
74
+ npm publish --access public
75
75
  ```
@@ -1,4 +1,4 @@
1
- import { Backup, DatabaseCredentials, DbInfo, DumpOptions, ImportResult, OriginStorageConfig, PageResponse, RelationshipInfo, StorageFileItem, TableDetail, TableInfo, VerifyResult } from '../types/minivault';
1
+ import { Backup, ConnectionCheck, DatabaseCredentials, DbInfo, DumpOptions, ImportResult, OriginStorageConfig, PageResponse, RelationshipInfo, StorageFileItem, TableDetail, TableInfo, VerifyResult } from '../types/minivault';
2
2
  export interface MinivaultApiOptions {
3
3
  /**
4
4
  * URL base da API. Exemplos:
@@ -44,6 +44,7 @@ export declare class MinivaultApi {
44
44
  listDbTables(): Promise<TableInfo[]>;
45
45
  getDbTable(ref: string): Promise<TableDetail>;
46
46
  listDbRelationships(): Promise<RelationshipInfo[]>;
47
+ getDiagnostics(): Promise<ConnectionCheck[]>;
47
48
  listStorageFiles(type: string, prefix?: string, storageConfig?: OriginStorageConfig): Promise<StorageFileItem[]>;
48
49
  downloadStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig): Promise<Blob>;
49
50
  deleteStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig): Promise<void>;
@@ -5,5 +5,7 @@ export interface DatabaseBannerProps {
5
5
  error?: unknown;
6
6
  /** Rótulo contextual, ex.: 'banco de origem' / 'banco de destino'. */
7
7
  label: string;
8
+ /** Modo compacto: não expõe host/porta/nome do banco nem detalhes de configuração. */
9
+ compact?: boolean;
8
10
  }
9
- export declare function DatabaseBanner({ dbInfo, isLoading, error, label }: DatabaseBannerProps): import("react").JSX.Element;
11
+ export declare function DatabaseBanner({ dbInfo, isLoading, error, label, compact }: DatabaseBannerProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function DiagnosticsTab(): import("react").JSX.Element;
@@ -2,7 +2,9 @@ import { DatabaseCredentials, DumpOptions, OriginStorageConfig } from '../types/
2
2
  export declare const backupsKeys: {
3
3
  all: readonly ["minivault", "backups"];
4
4
  db: readonly ["minivault", "db"];
5
+ diagnostics: readonly ["minivault", "diagnostics"];
5
6
  };
7
+ export declare function useDiagnostics(enabled?: boolean): import('@tanstack/react-query').UseQueryResult<import('../types/minivault').ConnectionCheck[], Error>;
6
8
  export declare function useBackups(page?: number, size?: number, enabled?: boolean): import('@tanstack/react-query').UseQueryResult<import('../types/minivault').PageResponse<import('../types/minivault').Backup>, Error>;
7
9
  export declare function useDbInfo(enabled?: boolean): import('@tanstack/react-query').UseQueryResult<import('../types/minivault').DbInfo, Error>;
8
10
  export declare function useDbTables(enabled?: boolean): import('@tanstack/react-query').UseQueryResult<import('../types/minivault').TableInfo[], Error>;
package/dist/index.d.ts CHANGED
@@ -5,12 +5,13 @@ export { MinivaultApi, MinivaultApiError } from './api/MinivaultApi';
5
5
  export type { MinivaultApiOptions } from './api/MinivaultApi';
6
6
  export { BackupsTab } from './components/BackupsTab';
7
7
  export { CreateBackupTab } from './components/CreateBackupTab';
8
+ export { DiagnosticsTab } from './components/DiagnosticsTab';
8
9
  export { ImportDumpTab } from './components/ImportDumpTab';
9
10
  export { StorageExplorerTab } from './components/StorageExplorerTab';
10
11
  export { RestoreModal } from './components/RestoreModal';
11
12
  export { CredentialsFields } from './components/CredentialsFields';
12
13
  export { DatabaseBanner } from './components/DatabaseBanner';
13
14
  export { StatusBadge } from './components/StatusBadge';
14
- export { useBackups, useCreateBackup, useRestoreBackup, useDeleteBackup, useDownloadBackup, useImportDump, useImportFromStorage, useDbInfo, useDbTables, useDbTableDetail, useDbRelationships, } from './hooks/useBackups';
15
+ export { useBackups, useCreateBackup, useRestoreBackup, useDeleteBackup, useDownloadBackup, useImportDump, useImportFromStorage, useDbInfo, useDbTables, useDbTableDetail, useDbRelationships, useDiagnostics, } from './hooks/useBackups';
15
16
  export { formatFileSize, formatDate, STATUS_LABELS, STATUS_BADGE, DATABASE_TYPE_LABELS } from './utils/format';
16
- export type { Backup, BackupRequest, BackupStatus, ColumnInfo, DatabaseCredentials, DatabaseType, DbInfo, DumpOptions, DumpFormat, ImportResult, ImportFromStorageRequest, OriginStorageConfig, PageResponse, RelationshipInfo, RestoreRequest, StorageFileItem, TableDetail, TableInfo, } from './types/minivault';
17
+ export type { Backup, BackupRequest, BackupStatus, ColumnInfo, ConnectionCheck, DatabaseCredentials, DatabaseType, DbInfo, DumpOptions, DumpFormat, ImportResult, ImportFromStorageRequest, OriginStorageConfig, PageResponse, RelationshipInfo, RestoreRequest, StorageFileItem, TableDetail, TableInfo, VerifyResult, } from './types/minivault';
@@ -135,6 +135,9 @@ var h = class extends Error {
135
135
  listDbRelationships() {
136
136
  return this.request("/api/db/relationships");
137
137
  }
138
+ getDiagnostics() {
139
+ return this.request("/api/diagnostics");
140
+ }
138
141
  listStorageFiles(e, t, n) {
139
142
  let r = new URLSearchParams({ type: e });
140
143
  return t && r.set("prefix", t), n && (n.endpoint && r.set("endpoint", n.endpoint), n.bucketName && r.set("bucketName", n.bucketName), n.accessKey && r.set("accessKey", n.accessKey), n.secretKey && r.set("secretKey", n.secretKey), n.region && r.set("region", n.region), n.pathStyleEnabled && r.set("pathStyleEnabled", "true"), n.localPath && r.set("localPath", n.localPath)), this.request(`/api/storage/files?${r.toString()}`);
@@ -202,9 +205,18 @@ function x() {
202
205
  //#region src/hooks/useBackups.ts
203
206
  var S = {
204
207
  all: ["minivault", "backups"],
205
- db: ["minivault", "db"]
208
+ db: ["minivault", "db"],
209
+ diagnostics: ["minivault", "diagnostics"]
206
210
  };
207
- function C(e = 0, t = 10, n = !0) {
211
+ function C(e = !0) {
212
+ let t = x();
213
+ return u({
214
+ queryKey: S.diagnostics,
215
+ queryFn: () => t.getDiagnostics(),
216
+ enabled: e && !!t
217
+ });
218
+ }
219
+ function w(e = 0, t = 10, n = !0) {
208
220
  let r = x();
209
221
  return u({
210
222
  queryKey: [
@@ -216,7 +228,7 @@ function C(e = 0, t = 10, n = !0) {
216
228
  enabled: n && !!r
217
229
  });
218
230
  }
219
- function w(e = !0) {
231
+ function T(e = !0) {
220
232
  let t = x();
221
233
  return u({
222
234
  queryKey: [...S.db, "info"],
@@ -224,7 +236,7 @@ function w(e = !0) {
224
236
  enabled: e && !!t
225
237
  });
226
238
  }
227
- function T(e = !0) {
239
+ function E(e = !0) {
228
240
  let t = x();
229
241
  return u({
230
242
  queryKey: [...S.db, "tables"],
@@ -232,7 +244,7 @@ function T(e = !0) {
232
244
  enabled: e && !!t
233
245
  });
234
246
  }
235
- function E(e, t = !0) {
247
+ function D(e, t = !0) {
236
248
  let n = x();
237
249
  return u({
238
250
  queryKey: [
@@ -244,7 +256,7 @@ function E(e, t = !0) {
244
256
  enabled: t && !!n && !!e
245
257
  });
246
258
  }
247
- function D(e = !0) {
259
+ function O(e = !0) {
248
260
  let t = x();
249
261
  return u({
250
262
  queryKey: [...S.db, "relationships"],
@@ -252,74 +264,74 @@ function D(e = !0) {
252
264
  enabled: e && !!t
253
265
  });
254
266
  }
255
- function O() {
267
+ function k() {
256
268
  let e = b(), t = d();
257
269
  return l({
258
270
  mutationFn: (t) => e.createBackup(t.database, t.originStorage, t.dumpOptions),
259
271
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
260
272
  });
261
273
  }
262
- function k() {
274
+ function A() {
263
275
  let e = b(), t = d();
264
276
  return l({
265
277
  mutationFn: (t) => e.restoreBackup(t.id, t.database, t.originStorage),
266
278
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
267
279
  });
268
280
  }
269
- function A() {
281
+ function j() {
270
282
  let e = b(), t = d();
271
283
  return l({
272
284
  mutationFn: (t) => e.deleteBackup(t),
273
285
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
274
286
  });
275
287
  }
276
- function j() {
288
+ function M() {
277
289
  let e = b(), t = d();
278
290
  return l({
279
291
  mutationFn: (t) => e.verifyBackup(t),
280
292
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
281
293
  });
282
294
  }
283
- function M() {
295
+ function N() {
284
296
  let e = b();
285
297
  return l({ mutationFn: (t) => e.downloadBackup(t.id) });
286
298
  }
287
- function N() {
299
+ function P() {
288
300
  let e = b();
289
301
  return l({ mutationFn: (t) => e.importDump(t.file, t.database) });
290
302
  }
291
- function P() {
303
+ function F() {
292
304
  let e = b();
293
305
  return l({ mutationFn: (t) => e.importFromStorage(t.storageType, t.storagePath, t.database, t.storageConfig) });
294
306
  }
295
307
  //#endregion
296
308
  //#region src/utils/format.ts
297
- var F = new Intl.DateTimeFormat("pt-BR", {
309
+ var I = new Intl.DateTimeFormat("pt-BR", {
298
310
  day: "2-digit",
299
311
  month: "2-digit",
300
312
  year: "numeric",
301
313
  hour: "2-digit",
302
314
  minute: "2-digit"
303
- }), I = [
315
+ }), L = [
304
316
  "B",
305
317
  "KB",
306
318
  "MB",
307
319
  "GB",
308
320
  "TB"
309
321
  ];
310
- function L(e) {
322
+ function R(e) {
311
323
  if (e == null || e < 0) return "—";
312
324
  if (e < 1024) return `${e} B`;
313
325
  let t = e, n = 0;
314
- for (; t >= 1024 && n < I.length - 1;) t /= 1024, n += 1;
315
- return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${I[n]}`;
326
+ for (; t >= 1024 && n < L.length - 1;) t /= 1024, n += 1;
327
+ return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${L[n]}`;
316
328
  }
317
- function R(e) {
329
+ function z(e) {
318
330
  if (!e) return "—";
319
331
  let t = new Date(e);
320
- return Number.isNaN(t.getTime()) ? "—" : F.format(t);
332
+ return Number.isNaN(t.getTime()) ? "—" : I.format(t);
321
333
  }
322
- var z = {
334
+ var B = {
323
335
  PENDING: "Pendente",
324
336
  IN_PROGRESS: "Em andamento",
325
337
  COMPLETED: "Concluído",
@@ -327,7 +339,7 @@ var z = {
327
339
  RESTORING: "Restaurando",
328
340
  RESTORED: "Restaurado",
329
341
  RESTORE_FAILED: "Falha na restauração"
330
- }, B = {
342
+ }, V = {
331
343
  PENDING: "text-bg-secondary",
332
344
  IN_PROGRESS: "text-bg-primary",
333
345
  COMPLETED: "text-bg-success",
@@ -335,7 +347,7 @@ var z = {
335
347
  RESTORING: "text-bg-warning",
336
348
  RESTORED: "text-bg-info",
337
349
  RESTORE_FAILED: "text-bg-danger"
338
- }, V = {
350
+ }, H = {
339
351
  postgresql: "PostgreSQL",
340
352
  postgres: "PostgreSQL",
341
353
  mysql: "MySQL",
@@ -344,8 +356,8 @@ var z = {
344
356
  };
345
357
  //#endregion
346
358
  //#region src/components/StatusBadge.tsx
347
- function H({ status: e }) {
348
- let t = B[e] ?? "text-bg-secondary", n = z[e] ?? e;
359
+ function U({ status: e }) {
360
+ let t = V[e] ?? "text-bg-secondary", n = B[e] ?? e;
349
361
  return /* @__PURE__ */ p("span", {
350
362
  className: `badge rounded-pill ${t}`,
351
363
  children: n
@@ -353,7 +365,7 @@ function H({ status: e }) {
353
365
  }
354
366
  //#endregion
355
367
  //#region src/components/DatabaseBanner.tsx
356
- function U({ dbInfo: e, isLoading: t, error: n, label: r }) {
368
+ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
357
369
  if (t) return /* @__PURE__ */ p("div", {
358
370
  className: "mb-3",
359
371
  children: /* @__PURE__ */ m("small", {
@@ -372,10 +384,16 @@ function U({ dbInfo: e, isLoading: t, error: n, label: r }) {
372
384
  "aria-hidden": !0
373
385
  }),
374
386
  "Não foi possível consultar o banco configurado: ",
375
- G(n)
387
+ K(n)
376
388
  ]
377
389
  });
378
- if (!e || !e.configured) return /* @__PURE__ */ m("div", {
390
+ if (!e || !e.configured) return i ? /* @__PURE__ */ m("div", {
391
+ className: "alert alert-danger mb-3 py-2 small",
392
+ children: [/* @__PURE__ */ p("i", {
393
+ className: "bi bi-x-octagon me-2",
394
+ "aria-hidden": !0
395
+ }), "Banco do servidor não configurado. Não é possível criar backups."]
396
+ }) : /* @__PURE__ */ m("div", {
379
397
  className: "alert alert-danger mb-3 py-2 small",
380
398
  children: [
381
399
  /* @__PURE__ */ p("i", {
@@ -388,8 +406,57 @@ function U({ dbInfo: e, isLoading: t, error: n, label: r }) {
388
406
  "). A criação e a importação usam esse banco."
389
407
  ]
390
408
  });
391
- let i = V[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
392
- return /* @__PURE__ */ m("div", {
409
+ let a = H[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
410
+ return i ? /* @__PURE__ */ m("div", {
411
+ className: "mb-3",
412
+ children: [
413
+ /* @__PURE__ */ m("div", {
414
+ className: "d-flex flex-wrap align-items-center gap-2 small text-body-secondary",
415
+ children: [/* @__PURE__ */ p("i", {
416
+ className: "bi bi-database-check text-success",
417
+ "aria-hidden": !0
418
+ }), /* @__PURE__ */ m("span", { children: [
419
+ r,
420
+ ": ",
421
+ /* @__PURE__ */ p("strong", { children: "banco do servidor disponível" })
422
+ ] })]
423
+ }),
424
+ e.exportExcludedTables.length > 0 && /* @__PURE__ */ m("div", {
425
+ className: "mt-1 small",
426
+ children: [
427
+ /* @__PURE__ */ p("i", {
428
+ className: "bi bi-shield-lock me-1",
429
+ "aria-hidden": !0
430
+ }),
431
+ /* @__PURE__ */ p("span", {
432
+ className: "text-body-secondary",
433
+ children: "Exportação sempre ignora: "
434
+ }),
435
+ e.exportExcludedTables.map((e) => /* @__PURE__ */ p("span", {
436
+ className: "badge text-bg-secondary me-1",
437
+ children: e
438
+ }, `ex-${e}`))
439
+ ]
440
+ }),
441
+ e.importExcludedTables.length > 0 && /* @__PURE__ */ m("div", {
442
+ className: "mt-1 small",
443
+ children: [
444
+ /* @__PURE__ */ p("i", {
445
+ className: "bi bi-shield-lock me-1",
446
+ "aria-hidden": !0
447
+ }),
448
+ /* @__PURE__ */ p("span", {
449
+ className: "text-body-secondary",
450
+ children: "Importação sempre ignora: "
451
+ }),
452
+ e.importExcludedTables.map((e) => /* @__PURE__ */ p("span", {
453
+ className: "badge text-bg-secondary me-1",
454
+ children: e
455
+ }, `im-${e}`))
456
+ ]
457
+ })
458
+ ]
459
+ }) : /* @__PURE__ */ m("div", {
393
460
  className: "mb-3",
394
461
  children: [
395
462
  /* @__PURE__ */ m("div", {
@@ -402,7 +469,7 @@ function U({ dbInfo: e, isLoading: t, error: n, label: r }) {
402
469
  /* @__PURE__ */ m("span", { children: [
403
470
  r,
404
471
  ": ",
405
- /* @__PURE__ */ p("strong", { children: i }),
472
+ /* @__PURE__ */ p("strong", { children: a }),
406
473
  " em ",
407
474
  /* @__PURE__ */ m("code", { children: [
408
475
  e.host,
@@ -457,8 +524,8 @@ function U({ dbInfo: e, isLoading: t, error: n, label: r }) {
457
524
  }
458
525
  //#endregion
459
526
  //#region src/components/RestoreModal.tsx
460
- function W({ backup: e, onClose: t }) {
461
- let n = k(), r = w(), i = () => {
527
+ function G({ backup: e, onClose: t }) {
528
+ let n = A(), r = T(), i = () => {
462
529
  n.mutate({ id: e.id });
463
530
  }, a = n.isPending;
464
531
  return /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", { className: "modal-backdrop show" }), /* @__PURE__ */ p("div", {
@@ -504,7 +571,7 @@ function W({ backup: e, onClose: t }) {
504
571
  " será aplicado no banco configurado abaixo."
505
572
  ]
506
573
  }),
507
- /* @__PURE__ */ p(U, {
574
+ /* @__PURE__ */ p(W, {
508
575
  label: "Banco de destino",
509
576
  dbInfo: r.data,
510
577
  isLoading: r.isLoading,
@@ -515,7 +582,7 @@ function W({ backup: e, onClose: t }) {
515
582
  children: [/* @__PURE__ */ p("i", {
516
583
  className: "bi bi-x-circle me-2",
517
584
  "aria-hidden": !0
518
- }), G(n.error)]
585
+ }), K(n.error)]
519
586
  }),
520
587
  n.isSuccess && /* @__PURE__ */ m("div", {
521
588
  className: "alert alert-success mt-3 mb-0",
@@ -550,19 +617,19 @@ function W({ backup: e, onClose: t }) {
550
617
  })
551
618
  })] });
552
619
  }
553
- function G(e) {
620
+ function K(e) {
554
621
  return e instanceof Error ? e.message : String(e);
555
622
  }
556
623
  //#endregion
557
624
  //#region src/components/BackupsTab.tsx
558
- function K() {
559
- let [e, t] = o(0), [n, a] = o(null), [s, c] = o(null), [l, u] = o({}), { data: d, isLoading: f, isError: h, error: g, refetch: _, isFetching: v } = C(e, 10, !0), y = M(), b = A(), x = j();
625
+ function q() {
626
+ let [e, t] = o(0), [n, a] = o(null), [s, c] = o(null), [l, u] = o({}), { data: d, isLoading: f, isError: h, error: g, refetch: _, isFetching: v } = w(e, 10, !0), y = N(), b = j(), x = M();
560
627
  r(() => {
561
628
  if (!y.data || !y.variables) return;
562
629
  let e = URL.createObjectURL(y.data), t = document.createElement("a");
563
630
  t.href = e, t.download = y.variables.filename, document.body.appendChild(t), t.click(), t.remove(), setTimeout(() => URL.revokeObjectURL(e), 1e4);
564
631
  }, [y.data, y.variables]);
565
- let S = i(() => d?.content ?? [], [d]), w = d?.totalPages ?? 0;
632
+ let S = i(() => d?.content ?? [], [d]), C = d?.totalPages ?? 0;
566
633
  return /* @__PURE__ */ m("div", { children: [
567
634
  /* @__PURE__ */ m("div", {
568
635
  className: "d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3",
@@ -719,12 +786,12 @@ function K() {
719
786
  "aria-hidden": !0
720
787
  }), e.filename]
721
788
  }),
722
- /* @__PURE__ */ p("td", { children: V[e.databaseType] ?? e.databaseType }),
789
+ /* @__PURE__ */ p("td", { children: H[e.databaseType] ?? e.databaseType }),
723
790
  /* @__PURE__ */ p("td", {
724
791
  className: "text-end",
725
- children: L(e.fileSize)
792
+ children: R(e.fileSize)
726
793
  }),
727
- /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(H, { status: e.status }) }),
794
+ /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(U, { status: e.status }) }),
728
795
  /* @__PURE__ */ p("td", { children: e.sha256 ? /* @__PURE__ */ m("code", {
729
796
  title: e.sha256,
730
797
  style: { fontSize: "0.8em" },
@@ -747,7 +814,7 @@ function K() {
747
814
  className: "text-muted",
748
815
  children: "—"
749
816
  }) }),
750
- /* @__PURE__ */ p("td", { children: R(e.createdAt) }),
817
+ /* @__PURE__ */ p("td", { children: z(e.createdAt) }),
751
818
  /* @__PURE__ */ m("td", {
752
819
  className: "text-end text-nowrap",
753
820
  children: [
@@ -815,7 +882,7 @@ function K() {
815
882
  "Página ",
816
883
  e + 1,
817
884
  " de ",
818
- Math.max(w, 1)
885
+ Math.max(C, 1)
819
886
  ]
820
887
  }), /* @__PURE__ */ m("div", {
821
888
  className: "btn-group",
@@ -831,7 +898,7 @@ function K() {
831
898
  }), /* @__PURE__ */ p("button", {
832
899
  type: "button",
833
900
  className: "btn btn-outline-secondary btn-sm",
834
- disabled: e + 1 >= w,
901
+ disabled: e + 1 >= C,
835
902
  onClick: () => t((e) => e + 1),
836
903
  children: /* @__PURE__ */ p("i", {
837
904
  className: "bi bi-chevron-right",
@@ -840,11 +907,11 @@ function K() {
840
907
  })]
841
908
  })]
842
909
  }),
843
- n && /* @__PURE__ */ p(W, {
910
+ n && /* @__PURE__ */ p(G, {
844
911
  backup: n,
845
912
  onClose: () => a(null)
846
913
  }),
847
- s && /* @__PURE__ */ p(q, {
914
+ s && /* @__PURE__ */ p(ee, {
848
915
  backup: s,
849
916
  busy: b.isPending,
850
917
  onCancel: () => c(null),
@@ -854,7 +921,7 @@ function K() {
854
921
  })
855
922
  ] });
856
923
  }
857
- function q(e) {
924
+ function ee(e) {
858
925
  let { backup: t, busy: n, onCancel: r, onConfirm: i } = e;
859
926
  return /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", { className: "modal-backdrop show" }), /* @__PURE__ */ p("div", {
860
927
  className: "modal show d-block",
@@ -926,7 +993,7 @@ function J(e) {
926
993
  //#endregion
927
994
  //#region src/components/CreateBackupTab.tsx
928
995
  function Y() {
929
- let e = O(), t = w(), n = T(t.data?.configured), [r, a] = o(!1), [s, c] = o(() => ({
996
+ let e = k(), t = T(), n = E(t.data?.configured), [r, a] = o(!1), [s, c] = o(() => ({
930
997
  type: "LOCAL",
931
998
  localPath: ""
932
999
  })), [l, u] = o(!0), [d, h] = o({
@@ -937,7 +1004,7 @@ function Y() {
937
1004
  includeIndexes: !0,
938
1005
  includeConstraints: !0,
939
1006
  includeRelatedTables: !1
940
- }), [g, _] = o(() => /* @__PURE__ */ new Set()), [v, y] = o(null), [b, x] = o({}), [S, C] = o(""), D = E(v, t.data?.configured), k = i(() => new Set((t.data?.exportExcludedTables ?? []).map(te)), [t.data?.exportExcludedTables]), A = i(() => {
1007
+ }), [g, _] = o(() => /* @__PURE__ */ new Set()), [v, y] = o(null), [b, x] = o({}), [S, C] = o(""), w = D(v, t.data?.configured), O = i(() => new Set((t.data?.exportExcludedTables ?? []).map(ne)), [t.data?.exportExcludedTables]), A = i(() => {
941
1008
  let e = n.data ?? [], t = S.trim().toLowerCase();
942
1009
  return t ? e.filter((e) => e.fullName.toLowerCase().includes(t)) : e;
943
1010
  }, [n.data, S]), j = i(() => Object.values(b).reduce((e, t) => e + t.length, 0), [b]), M = (e) => {
@@ -975,14 +1042,15 @@ function Y() {
975
1042
  children: "Criar novo backup"
976
1043
  }), /* @__PURE__ */ p("p", {
977
1044
  className: "text-muted small mb-0",
978
- children: "O minivault usa o banco configurado no servidor (via variáveis de ambiente). Selecione as tabelas no grid e expanda um card para marcar colunas cujo conteúdo será nulificado na exportação."
1045
+ children: "Selecione as tabelas no grid e expanda um card para marcar colunas cujo conteúdo será nulificado na exportação."
979
1046
  })]
980
1047
  }),
981
- /* @__PURE__ */ p(U, {
1048
+ /* @__PURE__ */ p(W, {
982
1049
  label: "Banco de origem",
983
1050
  dbInfo: t.data,
984
1051
  isLoading: t.isLoading,
985
- error: t.error
1052
+ error: t.error,
1053
+ compact: !0
986
1054
  }),
987
1055
  /* @__PURE__ */ m("div", {
988
1056
  className: "card mb-3",
@@ -996,7 +1064,7 @@ function Y() {
996
1064
  "aria-hidden": !0
997
1065
  }),
998
1066
  "Tabelas do banco (",
999
- n.data?.filter((e) => !k.has(e.fullName)).length ?? 0,
1067
+ n.data?.filter((e) => !O.has(e.fullName)).length ?? 0,
1000
1068
  " disponíveis)"
1001
1069
  ]
1002
1070
  }), /* @__PURE__ */ m("div", {
@@ -1036,7 +1104,7 @@ function Y() {
1036
1104
  "aria-hidden": !0
1037
1105
  }),
1038
1106
  "Falha ao listar tabelas: ",
1039
- G(n.error)
1107
+ K(n.error)
1040
1108
  ]
1041
1109
  }),
1042
1110
  A.length === 0 && !n.isLoading && /* @__PURE__ */ p("div", {
@@ -1052,16 +1120,16 @@ function Y() {
1052
1120
  maxHeight: 420,
1053
1121
  overflow: "auto"
1054
1122
  },
1055
- children: A.map((e) => /* @__PURE__ */ p(ee, {
1123
+ children: A.map((e) => /* @__PURE__ */ p(te, {
1056
1124
  table: e,
1057
- disabled: k.has(e.fullName),
1125
+ disabled: O.has(e.fullName),
1058
1126
  selected: g.has(e.fullName),
1059
1127
  expanded: v === e.fullName,
1060
1128
  excluded: b[e.fullName] ?? [],
1061
1129
  onToggleTable: () => M(e.fullName),
1062
1130
  onToggleExpand: () => y(v === e.fullName ? null : e.fullName),
1063
1131
  onToggleColumn: (t) => N(e.fullName, t),
1064
- detail: v === e.fullName ? D : void 0
1132
+ detail: v === e.fullName ? w : void 0
1065
1133
  }, e.fullName))
1066
1134
  }),
1067
1135
  /* @__PURE__ */ m("div", {
@@ -1374,7 +1442,7 @@ function Y() {
1374
1442
  "aria-hidden": !0
1375
1443
  }),
1376
1444
  "Falha ao criar backup: ",
1377
- G(e.error)
1445
+ K(e.error)
1378
1446
  ]
1379
1447
  }),
1380
1448
  e.isSuccess && /* @__PURE__ */ m("div", {
@@ -1413,7 +1481,7 @@ function Y() {
1413
1481
  })
1414
1482
  ] });
1415
1483
  }
1416
- function ee({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1484
+ function te({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1417
1485
  return /* @__PURE__ */ p("div", {
1418
1486
  className: `card h-100 ${n ? "border-primary" : ""}`,
1419
1487
  style: { minWidth: 0 },
@@ -1477,7 +1545,7 @@ function ee({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1477
1545
  }),
1478
1546
  a?.isError && /* @__PURE__ */ m("small", {
1479
1547
  className: "text-danger",
1480
- children: ["Erro: ", G(a.error)]
1548
+ children: ["Erro: ", K(a.error)]
1481
1549
  }),
1482
1550
  a?.data && /* @__PURE__ */ m(f, { children: [a.data.columns.map((e) => /* @__PURE__ */ m("div", {
1483
1551
  className: "d-flex align-items-center gap-2 py-1",
@@ -1528,13 +1596,135 @@ function ee({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1528
1596
  })
1529
1597
  });
1530
1598
  }
1531
- function te(e) {
1599
+ function ne(e) {
1532
1600
  let t = e.trim();
1533
1601
  return t.includes(".") ? t : `public.${t}`;
1534
1602
  }
1535
1603
  //#endregion
1604
+ //#region src/components/DiagnosticsTab.tsx
1605
+ function X() {
1606
+ let { data: e, isLoading: t, isError: n, error: r, refetch: a, isFetching: o } = C(), s = i(() => e ? {
1607
+ total: e.length,
1608
+ okCount: e.filter((e) => e.ok).length,
1609
+ notConfigured: e.filter((e) => !e.configured).length
1610
+ } : null, [e]);
1611
+ return /* @__PURE__ */ m("div", { children: [
1612
+ /* @__PURE__ */ m("div", {
1613
+ className: "d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3",
1614
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h5", {
1615
+ className: "mb-0",
1616
+ children: "Diagnóstico do servidor"
1617
+ }), /* @__PURE__ */ p("span", {
1618
+ className: "text-muted small",
1619
+ children: "Verifica conexões de banco, storages e servidor de e-mail configurados."
1620
+ })] }), /* @__PURE__ */ m("button", {
1621
+ type: "button",
1622
+ className: "btn btn-outline-secondary btn-sm",
1623
+ onClick: () => a(),
1624
+ disabled: o,
1625
+ children: [
1626
+ o && /* @__PURE__ */ p("span", {
1627
+ className: "spinner-border spinner-border-sm me-2",
1628
+ "aria-hidden": !0
1629
+ }),
1630
+ /* @__PURE__ */ p("i", {
1631
+ className: "bi bi-arrow-repeat me-1",
1632
+ "aria-hidden": !0
1633
+ }),
1634
+ "Executar testes novamente"
1635
+ ]
1636
+ })]
1637
+ }),
1638
+ n && /* @__PURE__ */ m("div", {
1639
+ className: "alert alert-danger",
1640
+ children: [
1641
+ /* @__PURE__ */ p("i", {
1642
+ className: "bi bi-exclamation-triangle me-2",
1643
+ "aria-hidden": !0
1644
+ }),
1645
+ "Não foi possível executar as checagens: ",
1646
+ K(r)
1647
+ ]
1648
+ }),
1649
+ s && /* @__PURE__ */ m("div", {
1650
+ className: "d-flex flex-wrap gap-2 mb-3",
1651
+ children: [
1652
+ /* @__PURE__ */ m("span", {
1653
+ className: "badge text-bg-success",
1654
+ children: [
1655
+ /* @__PURE__ */ p("i", {
1656
+ className: "bi bi-check-circle me-1",
1657
+ "aria-hidden": !0
1658
+ }),
1659
+ s.okCount,
1660
+ " OK"
1661
+ ]
1662
+ }),
1663
+ /* @__PURE__ */ m("span", {
1664
+ className: "badge text-bg-warning",
1665
+ children: [s.total - s.okCount - s.notConfigured, " com falha"]
1666
+ }),
1667
+ /* @__PURE__ */ m("span", {
1668
+ className: "badge text-bg-secondary",
1669
+ children: [s.notConfigured, " não configurado(s)"]
1670
+ })
1671
+ ]
1672
+ }),
1673
+ t && !e ? /* @__PURE__ */ m("div", {
1674
+ className: "text-center py-5 text-muted",
1675
+ children: [/* @__PURE__ */ p("div", {
1676
+ className: "spinner-border mb-2",
1677
+ "aria-hidden": !0
1678
+ }), /* @__PURE__ */ p("div", { children: "Executando checagens…" })]
1679
+ }) : /* @__PURE__ */ p("div", {
1680
+ className: "card",
1681
+ children: /* @__PURE__ */ m("ul", {
1682
+ className: "list-group list-group-flush",
1683
+ children: [(e ?? []).map((e) => /* @__PURE__ */ p(re, { check: e }, e.name)), e && e.length === 0 && /* @__PURE__ */ p("li", {
1684
+ className: "list-group-item text-center text-muted py-4",
1685
+ children: "Nenhuma checagem retornada."
1686
+ })]
1687
+ })
1688
+ })
1689
+ ] });
1690
+ }
1691
+ function re({ check: e }) {
1692
+ let t = e.configured ? e.ok ? "bi-check-circle-fill text-success" : "bi-x-octagon-fill text-danger" : "bi-dash-circle text-secondary", n = e.configured ? e.ok ? "text-bg-success" : "text-bg-danger" : "text-bg-secondary", r = e.configured ? e.ok ? "OK" : "Falha" : "Não configurado";
1693
+ return /* @__PURE__ */ m("li", {
1694
+ className: "list-group-item d-flex align-items-start gap-3",
1695
+ children: [/* @__PURE__ */ p("i", {
1696
+ className: `bi ${t} fs-4 mt-1`,
1697
+ "aria-hidden": !0
1698
+ }), /* @__PURE__ */ m("div", {
1699
+ className: "flex-grow-1",
1700
+ style: { minWidth: 0 },
1701
+ children: [/* @__PURE__ */ m("div", {
1702
+ className: "d-flex align-items-center flex-wrap gap-2",
1703
+ children: [
1704
+ /* @__PURE__ */ p("strong", { children: e.label }),
1705
+ /* @__PURE__ */ p("span", {
1706
+ className: "badge text-bg-light border",
1707
+ children: e.type
1708
+ }),
1709
+ /* @__PURE__ */ p("span", {
1710
+ className: `badge ${n}`,
1711
+ children: r
1712
+ }),
1713
+ /* @__PURE__ */ m("small", {
1714
+ className: "text-muted",
1715
+ children: [e.latencyMs, " ms"]
1716
+ })
1717
+ ]
1718
+ }), /* @__PURE__ */ p("div", {
1719
+ className: "small text-muted mt-1",
1720
+ children: e.message
1721
+ })]
1722
+ })]
1723
+ });
1724
+ }
1725
+ //#endregion
1536
1726
  //#region src/components/StorageExplorerTab.tsx
1537
- function X({ storageType: e = "LOCAL", storageConfig: n }) {
1727
+ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1538
1728
  let i = b(), s = d(), [c, h] = o(""), [g, _] = o([]), [v, y] = o(/* @__PURE__ */ new Set()), [x, S] = o(null), C = n?.type ?? e, { data: w, isLoading: T } = u({
1539
1729
  queryKey: [
1540
1730
  "storage-files",
@@ -1571,7 +1761,7 @@ function X({ storageType: e = "LOCAL", storageConfig: n }) {
1571
1761
  } catch {}
1572
1762
  }, N = async () => {
1573
1763
  for (let e of v) await M(e);
1574
- }, P = (e) => e ? R(e) : "-";
1764
+ }, P = (e) => e ? z(e) : "-";
1575
1765
  return /* @__PURE__ */ m("div", {
1576
1766
  className: "space-y-3",
1577
1767
  children: [
@@ -1701,7 +1891,7 @@ function X({ storageType: e = "LOCAL", storageConfig: n }) {
1701
1891
  }) }),
1702
1892
  /* @__PURE__ */ p("td", {
1703
1893
  className: "text-muted small",
1704
- children: e.directory ? "-" : L(e.size)
1894
+ children: e.directory ? "-" : R(e.size)
1705
1895
  }),
1706
1896
  /* @__PURE__ */ p("td", {
1707
1897
  className: "text-muted small",
@@ -1806,8 +1996,8 @@ function X({ storageType: e = "LOCAL", storageConfig: n }) {
1806
1996
  }
1807
1997
  //#endregion
1808
1998
  //#region src/components/ImportDumpTab.tsx
1809
- function Z() {
1810
- let e = N(), t = P(), n = w(), r = a(null), [i, s] = o("upload"), [c, l] = o(null), [u, d] = o(""), [f, h] = o(!1), g = () => {
1999
+ function Q() {
2000
+ let e = P(), t = F(), n = T(), r = a(null), [i, s] = o("upload"), [c, l] = o(null), [u, d] = o(""), [f, h] = o(!1), g = () => {
1811
2001
  i === "upload" && c ? e.mutate({ file: c }) : i === "storage" && u && t.mutate({
1812
2002
  storageType: "LOCAL",
1813
2003
  storagePath: u
@@ -1929,7 +2119,7 @@ function Z() {
1929
2119
  maxHeight: 350,
1930
2120
  overflow: "auto"
1931
2121
  },
1932
- children: /* @__PURE__ */ p(X, {
2122
+ children: /* @__PURE__ */ p(Z, {
1933
2123
  storageType: "LOCAL",
1934
2124
  storageConfig: {
1935
2125
  type: "LOCAL",
@@ -1952,7 +2142,7 @@ function Z() {
1952
2142
  children: [/* @__PURE__ */ p("p", {
1953
2143
  className: "small text-body-secondary mb-3",
1954
2144
  children: "O dump será aplicado no banco configurado no servidor. Tabelas bloqueadas por segurança são ignoradas automaticamente na importação."
1955
- }), /* @__PURE__ */ p(U, {
2145
+ }), /* @__PURE__ */ p(W, {
1956
2146
  label: "Banco de destino",
1957
2147
  dbInfo: n.data,
1958
2148
  isLoading: n.isLoading,
@@ -1968,7 +2158,7 @@ function Z() {
1968
2158
  "aria-hidden": !0
1969
2159
  }),
1970
2160
  "Falha na importacao: ",
1971
- G(y)
2161
+ K(y)
1972
2162
  ]
1973
2163
  }),
1974
2164
  v && /* @__PURE__ */ m("div", {
@@ -2010,13 +2200,13 @@ function Z() {
2010
2200
  }
2011
2201
  //#endregion
2012
2202
  //#region src/hooks/useAutoTheme.ts
2013
- function Q() {
2203
+ function $() {
2014
2204
  return typeof document < "u" && document.documentElement.classList.contains("dark");
2015
2205
  }
2016
- function ne(e) {
2017
- let [t, n] = o(Q), [i, a] = o(() => typeof window < "u" && window.matchMedia?.("(prefers-color-scheme: dark)")?.matches === !0);
2206
+ function ie(e) {
2207
+ let [t, n] = o($), [i, a] = o(() => typeof window < "u" && window.matchMedia?.("(prefers-color-scheme: dark)")?.matches === !0);
2018
2208
  return r(() => {
2019
- let e = document.documentElement, t = new MutationObserver(() => n(Q()));
2209
+ let e = document.documentElement, t = new MutationObserver(() => n($()));
2020
2210
  return t.observe(e, {
2021
2211
  attributes: !0,
2022
2212
  attributeFilter: ["class"]
@@ -2029,8 +2219,8 @@ function ne(e) {
2029
2219
  }
2030
2220
  //#endregion
2031
2221
  //#region src/components/MinivaultModule.tsx
2032
- function re(e) {
2033
- let { companyAcronym: t = "Minivault", theme: n, refetchIntervalMs: r = 15e3, storageType: i = "LOCAL", ...a } = e, l = ne(n), [u] = o(() => new s({ defaultOptions: { queries: {
2222
+ function ae(e) {
2223
+ let { companyAcronym: t = "Minivault", theme: n, refetchIntervalMs: r = 15e3, storageType: i = "LOCAL", ...a } = e, l = ie(n), [u] = o(() => new s({ defaultOptions: { queries: {
2034
2224
  refetchInterval: r,
2035
2225
  retry: 1
2036
2226
  } } }));
@@ -2041,15 +2231,15 @@ function re(e) {
2041
2231
  children: /* @__PURE__ */ m("div", {
2042
2232
  className: "minivault-module h-full w-full overflow-auto",
2043
2233
  "data-minivault-theme": l,
2044
- children: [/* @__PURE__ */ p(ie, {
2234
+ children: [/* @__PURE__ */ p(oe, {
2045
2235
  companyAcronym: t,
2046
2236
  theme: l
2047
- }), /* @__PURE__ */ p(ae, { storageType: i })]
2237
+ }), /* @__PURE__ */ p(se, { storageType: i })]
2048
2238
  })
2049
2239
  })
2050
2240
  });
2051
2241
  }
2052
- function ie({ companyAcronym: e, theme: t }) {
2242
+ function oe({ companyAcronym: e, theme: t }) {
2053
2243
  return /* @__PURE__ */ m("div", {
2054
2244
  className: "d-flex align-items-center justify-content-between flex-wrap gap-2 px-3 py-2 border-bottom",
2055
2245
  style: {
@@ -2078,7 +2268,7 @@ function ie({ companyAcronym: e, theme: t }) {
2078
2268
  })]
2079
2269
  });
2080
2270
  }
2081
- function ae({ storageType: e }) {
2271
+ function se({ storageType: e }) {
2082
2272
  let [t, n] = o("backups");
2083
2273
  return /* @__PURE__ */ m("div", {
2084
2274
  className: "p-3",
@@ -2133,24 +2323,37 @@ function ae({ storageType: e }) {
2133
2323
  "aria-hidden": !0
2134
2324
  }), "Storage"]
2135
2325
  })
2326
+ }),
2327
+ /* @__PURE__ */ p("li", {
2328
+ className: "nav-item",
2329
+ children: /* @__PURE__ */ m("button", {
2330
+ type: "button",
2331
+ className: `nav-link ${t === "diagnostics" ? "active" : ""}`,
2332
+ onClick: () => n("diagnostics"),
2333
+ children: [/* @__PURE__ */ p("i", {
2334
+ className: "bi bi-heart-pulse me-1",
2335
+ "aria-hidden": !0
2336
+ }), "Diagnóstico"]
2337
+ })
2136
2338
  })
2137
2339
  ]
2138
2340
  }),
2139
- t === "backups" && /* @__PURE__ */ p(K, {}),
2341
+ t === "backups" && /* @__PURE__ */ p(q, {}),
2140
2342
  t === "create" && /* @__PURE__ */ p(Y, {}),
2141
- t === "import" && /* @__PURE__ */ p(Z, {}),
2142
- t === "storage" && /* @__PURE__ */ p(X, { storageType: e })
2343
+ t === "import" && /* @__PURE__ */ p(Q, {}),
2344
+ t === "storage" && /* @__PURE__ */ p(Z, { storageType: e }),
2345
+ t === "diagnostics" && /* @__PURE__ */ p(X, {})
2143
2346
  ]
2144
2347
  });
2145
2348
  }
2146
2349
  //#endregion
2147
2350
  //#region src/components/CredentialsFields.tsx
2148
- var oe = [
2351
+ var ce = [
2149
2352
  "postgresql",
2150
2353
  "mysql",
2151
2354
  "sqlserver"
2152
2355
  ];
2153
- function se() {
2356
+ function le() {
2154
2357
  return {
2155
2358
  host: "localhost",
2156
2359
  port: 5432,
@@ -2160,19 +2363,19 @@ function se() {
2160
2363
  databaseType: "postgresql"
2161
2364
  };
2162
2365
  }
2163
- function $(e) {
2366
+ function ue(e) {
2164
2367
  return e === "postgresql" || e === "postgres" ? 5432 : e === "mysql" ? 3306 : 1433;
2165
2368
  }
2166
- function ce({ value: e, onChange: t, onValidatedChange: n }) {
2369
+ function de({ value: e, onChange: t, onValidatedChange: n }) {
2167
2370
  let r = {
2168
- ...se(),
2371
+ ...le(),
2169
2372
  ...e
2170
2373
  }, i = (e) => {
2171
2374
  let n = {
2172
2375
  ...r,
2173
2376
  databaseType: e
2174
2377
  };
2175
- (r.port === $(r.databaseType) || !r.port) && (n.port = $(e)), t(n);
2378
+ (r.port === ue(r.databaseType) || !r.port) && (n.port = ue(e)), t(n);
2176
2379
  }, a = (e) => {
2177
2380
  let i = {
2178
2381
  ...r,
@@ -2194,9 +2397,9 @@ function ce({ value: e, onChange: t, onValidatedChange: n }) {
2194
2397
  className: "form-select",
2195
2398
  value: r.databaseType,
2196
2399
  onChange: (e) => i(e.target.value),
2197
- children: oe.map((e) => /* @__PURE__ */ p("option", {
2400
+ children: ce.map((e) => /* @__PURE__ */ p("option", {
2198
2401
  value: e,
2199
- children: V[e] ?? e
2402
+ children: H[e] ?? e
2200
2403
  }, e))
2201
2404
  })]
2202
2405
  }),
@@ -2268,4 +2471,4 @@ function ce({ value: e, onChange: t, onValidatedChange: n }) {
2268
2471
  });
2269
2472
  }
2270
2473
  //#endregion
2271
- export { K as BackupsTab, Y as CreateBackupTab, ce as CredentialsFields, V as DATABASE_TYPE_LABELS, U as DatabaseBanner, Z as ImportDumpTab, g as MinivaultApi, h as MinivaultApiError, y as MinivaultApiProvider, re as MinivaultModule, W as RestoreModal, B as STATUS_BADGE, z as STATUS_LABELS, H as StatusBadge, X as StorageExplorerTab, R as formatDate, L as formatFileSize, C as useBackups, O as useCreateBackup, w as useDbInfo, D as useDbRelationships, E as useDbTableDetail, T as useDbTables, A as useDeleteBackup, M as useDownloadBackup, N as useImportDump, P as useImportFromStorage, b as useMinivaultApi, k as useRestoreBackup };
2474
+ export { q as BackupsTab, Y as CreateBackupTab, de as CredentialsFields, H as DATABASE_TYPE_LABELS, W as DatabaseBanner, X as DiagnosticsTab, Q as ImportDumpTab, g as MinivaultApi, h as MinivaultApiError, y as MinivaultApiProvider, ae as MinivaultModule, G as RestoreModal, V as STATUS_BADGE, B as STATUS_LABELS, U as StatusBadge, Z as StorageExplorerTab, z as formatDate, R as formatFileSize, w as useBackups, k as useCreateBackup, T as useDbInfo, O as useDbRelationships, D as useDbTableDetail, E as useDbTables, j as useDeleteBackup, C as useDiagnostics, N as useDownloadBackup, P as useImportDump, F as useImportFromStorage, b as useMinivaultApi, A as useRestoreBackup };
@@ -19,6 +19,15 @@ export interface VerifyResult {
19
19
  dumpReadable: boolean;
20
20
  message: string;
21
21
  }
22
+ export interface ConnectionCheck {
23
+ name: string;
24
+ label: string;
25
+ type: string;
26
+ configured: boolean;
27
+ ok: boolean;
28
+ message: string;
29
+ latencyMs: number;
30
+ }
22
31
  export interface PageResponse<T> {
23
32
  content: T[];
24
33
  page: number;
package/package.json CHANGED
@@ -1,73 +1,73 @@
1
- {
2
- "name": "@v1ct0rbr/minivault-web",
3
- "version": "0.4.3",
4
- "description": "Módulo independente de gerenciamento de backups (minivault). Consumível como pacote npm em qualquer app React. Cliente do minivault REST API (acesso tipicamente via proxy autenticado).",
5
- "author": "Victor Queiroga (https://www.linkedin.com/in/victor-queiroga)",
6
- "license": "MIT",
7
- "type": "module",
8
- "publishConfig": {
9
- "access": "public",
10
- "registry": "https://registry.npmjs.org/"
11
- },
12
- "files": [
13
- "dist",
14
- "README.md"
15
- ],
16
- "keywords": [
17
- "backup",
18
- "minivault",
19
- "der",
20
- "react",
21
- "bootstrap",
22
- "postgresql"
23
- ],
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/v1ct0rbr/minivault-web.git"
27
- },
28
- "main": "./dist/minivault-web.js",
29
- "module": "./dist/minivault-web.js",
30
- "types": "./dist/index.d.ts",
31
- "exports": {
32
- ".": {
33
- "types": "./dist/index.d.ts",
34
- "import": "./dist/minivault-web.js"
35
- },
36
- "./style.css": "./dist/minivault-web.css"
37
- },
38
- "scripts": {
39
- "dev": "vite --port 5176",
40
- "build": "vite build && tsc --noEmit",
41
- "build:lib": "vite build --config vite.lib.config.ts && tsc --noEmit",
42
- "prepublishOnly": "npm run build:lib && npm test",
43
- "prepack": "npm run build:lib",
44
- "preview": "vite preview",
45
- "test": "vitest run",
46
- "test:watch": "vitest",
47
- "typecheck": "tsc --noEmit",
48
- "lint": "eslint ."
49
- },
50
- "dependencies": {
51
- "bootstrap": "^5.3.3",
52
- "bootstrap-icons": "^1.11.3"
53
- },
54
- "peerDependencies": {
55
- "@tanstack/react-query": "^5.0.0",
56
- "react": "^19.0.0",
57
- "react-dom": "^19.0.0"
58
- },
59
- "devDependencies": {
60
- "@tanstack/react-query": "^5.66.5",
61
- "@types/node": "^22.10.2",
62
- "@types/react": "^19.2.0",
63
- "@types/react-dom": "^19.2.0",
64
- "@vitejs/plugin-react": "^5.0.4",
65
- "postcss-prefix-selector": "^2.1.1",
66
- "react": "^19.2.0",
67
- "react-dom": "^19.2.0",
68
- "typescript": "^5.7.2",
69
- "vite": "^8.0.16",
70
- "vite-plugin-dts": "^4.5.0",
71
- "vitest": "^4.1.8"
72
- }
1
+ {
2
+ "name": "@v1ct0rbr/minivault-web",
3
+ "version": "0.5.0",
4
+ "description": "Módulo independente de gerenciamento de backups (minivault). Consumível como pacote npm em qualquer app React. Cliente do minivault REST API (acesso tipicamente via proxy autenticado).",
5
+ "author": "Victor Queiroga (https://www.linkedin.com/in/victor-queiroga)",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "keywords": [
17
+ "backup",
18
+ "minivault",
19
+ "der",
20
+ "react",
21
+ "bootstrap",
22
+ "postgresql"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/v1ct0rbr/minivault-web.git"
27
+ },
28
+ "main": "./dist/minivault-web.js",
29
+ "module": "./dist/minivault-web.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/minivault-web.js"
35
+ },
36
+ "./style.css": "./dist/minivault-web.css"
37
+ },
38
+ "scripts": {
39
+ "dev": "vite --port 5176",
40
+ "build": "vite build && tsc --noEmit",
41
+ "build:lib": "vite build --config vite.lib.config.ts && tsc --noEmit",
42
+ "prepublishOnly": "npm run build:lib && npm test",
43
+ "prepack": "npm run build:lib",
44
+ "preview": "vite preview",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "typecheck": "tsc --noEmit",
48
+ "lint": "eslint ."
49
+ },
50
+ "dependencies": {
51
+ "bootstrap": "^5.3.3",
52
+ "bootstrap-icons": "^1.11.3"
53
+ },
54
+ "peerDependencies": {
55
+ "@tanstack/react-query": "^5.0.0",
56
+ "react": "^19.0.0",
57
+ "react-dom": "^19.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@tanstack/react-query": "^5.66.5",
61
+ "@types/node": "^22.10.2",
62
+ "@types/react": "^19.2.0",
63
+ "@types/react-dom": "^19.2.0",
64
+ "@vitejs/plugin-react": "^5.0.4",
65
+ "postcss-prefix-selector": "^2.1.1",
66
+ "react": "^19.2.0",
67
+ "react-dom": "^19.2.0",
68
+ "typescript": "^5.7.2",
69
+ "vite": "^8.0.16",
70
+ "vite-plugin-dts": "^4.5.0",
71
+ "vitest": "^4.1.8"
72
+ }
73
73
  }