@v1ct0rbr/minivault-web 0.5.0 → 0.6.1

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.
@@ -1,4 +1,4 @@
1
- import { Backup, ConnectionCheck, DatabaseCredentials, DbInfo, DumpOptions, ImportResult, OriginStorageConfig, PageResponse, RelationshipInfo, StorageFileItem, TableDetail, TableInfo, VerifyResult } from '../types/minivault';
1
+ import { Backup, BackupType, 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:
@@ -33,7 +33,7 @@ export declare class MinivaultApi {
33
33
  private request;
34
34
  listBackups(page?: number, size?: number): Promise<PageResponse<Backup>>;
35
35
  getBackup(id: number): Promise<Backup>;
36
- createBackup(database?: DatabaseCredentials, originStorage?: OriginStorageConfig, dumpOptions?: DumpOptions): Promise<Backup>;
36
+ createBackup(database?: DatabaseCredentials, originStorage?: OriginStorageConfig, dumpOptions?: DumpOptions, backupType?: BackupType): Promise<Backup>;
37
37
  restoreBackup(id: number, database?: DatabaseCredentials, originStorage?: OriginStorageConfig): Promise<void>;
38
38
  deleteBackup(id: number): Promise<void>;
39
39
  verifyBackup(id: number): Promise<VerifyResult>;
@@ -1,10 +1,5 @@
1
1
  import { MinivaultApiProviderProps } from './MinivaultApiProvider';
2
2
  export interface MinivaultModuleProps extends Omit<MinivaultApiProviderProps, 'children'> {
3
- /**
4
- * Força o tema ('light' | 'dark'). Quando omitido, o módulo segue
5
- * automaticamente o tema do host (classe `.dark` no <html> ou
6
- * prefers-color-scheme do sistema).
7
- */
8
3
  theme?: 'light' | 'dark';
9
4
  refetchIntervalMs?: number;
10
5
  companyAcronym?: string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Aba de backup apenas de storage: envia o storage de origem (ORIGIN_STORAGE_*)
3
+ * para o storage de destino (OUTPUT_STORAGE_*). Ambos são configurados no
4
+ * servidor via variáveis de ambiente — não é necessário informar dados aqui.
5
+ */
6
+ export declare function StorageBackupTab(): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { DatabaseCredentials, DumpOptions, OriginStorageConfig } from '../types/minivault';
1
+ import { BackupType, DatabaseCredentials, DumpOptions, OriginStorageConfig } from '../types/minivault';
2
2
  export declare const backupsKeys: {
3
3
  all: readonly ["minivault", "backups"];
4
4
  db: readonly ["minivault", "db"];
@@ -14,6 +14,7 @@ export declare function useCreateBackup(): import('@tanstack/react-query').UseMu
14
14
  database?: DatabaseCredentials;
15
15
  originStorage?: OriginStorageConfig;
16
16
  dumpOptions?: DumpOptions;
17
+ backupType?: BackupType;
17
18
  }, unknown>;
18
19
  export declare function useRestoreBackup(): import('@tanstack/react-query').UseMutationResult<void, Error, {
19
20
  id: number;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export { BackupsTab } from './components/BackupsTab';
7
7
  export { CreateBackupTab } from './components/CreateBackupTab';
8
8
  export { DiagnosticsTab } from './components/DiagnosticsTab';
9
9
  export { ImportDumpTab } from './components/ImportDumpTab';
10
+ export { StorageBackupTab } from './components/StorageBackupTab';
10
11
  export { StorageExplorerTab } from './components/StorageExplorerTab';
11
12
  export { RestoreModal } from './components/RestoreModal';
12
13
  export { CredentialsFields } from './components/CredentialsFields';
@@ -14,4 +15,4 @@ export { DatabaseBanner } from './components/DatabaseBanner';
14
15
  export { StatusBadge } from './components/StatusBadge';
15
16
  export { useBackups, useCreateBackup, useRestoreBackup, useDeleteBackup, useDownloadBackup, useImportDump, useImportFromStorage, useDbInfo, useDbTables, useDbTableDetail, useDbRelationships, useDiagnostics, } from './hooks/useBackups';
16
17
  export { formatFileSize, formatDate, STATUS_LABELS, STATUS_BADGE, DATABASE_TYPE_LABELS } from './utils/format';
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';
18
+ export type { Backup, BackupRequest, BackupStatus, BackupType, ColumnInfo, ConnectionCheck, DatabaseCredentials, DatabaseType, DbInfo, DumpOptions, DumpFormat, ImportResult, ImportFromStorageRequest, OriginStorageConfig, PageResponse, RelationshipInfo, RestoreRequest, StorageFileItem, TableDetail, TableInfo, VerifyResult, } from './types/minivault';
@@ -56,11 +56,11 @@ var h = class extends Error {
56
56
  getBackup(e) {
57
57
  return this.request(`/api/backups/${e}`);
58
58
  }
59
- createBackup(e, t, n) {
60
- let r = {};
61
- return e && (r.database = e), t && (r.originStorage = t), n && (r.dumpOptions = n), this.request("/api/backups", {
59
+ createBackup(e, t, n, r) {
60
+ let i = {};
61
+ return r && (i.backupType = r), e && (i.database = e), t && (i.originStorage = t), n && (i.dumpOptions = n), this.request("/api/backups", {
62
62
  method: "POST",
63
- body: JSON.stringify(r)
63
+ body: JSON.stringify(i)
64
64
  });
65
65
  }
66
66
  restoreBackup(e, t, n) {
@@ -267,7 +267,7 @@ function O(e = !0) {
267
267
  function k() {
268
268
  let e = b(), t = d();
269
269
  return l({
270
- mutationFn: (t) => e.createBackup(t.database, t.originStorage, t.dumpOptions),
270
+ mutationFn: (t) => e.createBackup(t.database, t.originStorage, t.dumpOptions, t.backupType),
271
271
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
272
272
  });
273
273
  }
@@ -306,32 +306,32 @@ function F() {
306
306
  }
307
307
  //#endregion
308
308
  //#region src/utils/format.ts
309
- var I = new Intl.DateTimeFormat("pt-BR", {
309
+ var ee = new Intl.DateTimeFormat("pt-BR", {
310
310
  day: "2-digit",
311
311
  month: "2-digit",
312
312
  year: "numeric",
313
313
  hour: "2-digit",
314
314
  minute: "2-digit"
315
- }), L = [
315
+ }), te = [
316
316
  "B",
317
317
  "KB",
318
318
  "MB",
319
319
  "GB",
320
320
  "TB"
321
321
  ];
322
- function R(e) {
322
+ function I(e) {
323
323
  if (e == null || e < 0) return "—";
324
324
  if (e < 1024) return `${e} B`;
325
325
  let t = e, n = 0;
326
- for (; t >= 1024 && n < L.length - 1;) t /= 1024, n += 1;
327
- return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${L[n]}`;
326
+ for (; t >= 1024 && n < te.length - 1;) t /= 1024, n += 1;
327
+ return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${te[n]}`;
328
328
  }
329
- function z(e) {
329
+ function L(e) {
330
330
  if (!e) return "—";
331
331
  let t = new Date(e);
332
- return Number.isNaN(t.getTime()) ? "—" : I.format(t);
332
+ return Number.isNaN(t.getTime()) ? "—" : ee.format(t);
333
333
  }
334
- var B = {
334
+ var R = {
335
335
  PENDING: "Pendente",
336
336
  IN_PROGRESS: "Em andamento",
337
337
  COMPLETED: "Concluído",
@@ -339,7 +339,7 @@ var B = {
339
339
  RESTORING: "Restaurando",
340
340
  RESTORED: "Restaurado",
341
341
  RESTORE_FAILED: "Falha na restauração"
342
- }, V = {
342
+ }, z = {
343
343
  PENDING: "text-bg-secondary",
344
344
  IN_PROGRESS: "text-bg-primary",
345
345
  COMPLETED: "text-bg-success",
@@ -347,17 +347,25 @@ var B = {
347
347
  RESTORING: "text-bg-warning",
348
348
  RESTORED: "text-bg-info",
349
349
  RESTORE_FAILED: "text-bg-danger"
350
- }, H = {
350
+ }, B = {
351
351
  postgresql: "PostgreSQL",
352
352
  postgres: "PostgreSQL",
353
353
  mysql: "MySQL",
354
354
  sqlserver: "SQL Server",
355
355
  mssql: "SQL Server"
356
+ }, ne = {
357
+ DATABASE: "Banco de dados",
358
+ STORAGE: "Storage",
359
+ FULL: "Completo"
360
+ }, re = {
361
+ DATABASE: "text-bg-primary",
362
+ STORAGE: "text-bg-info",
363
+ FULL: "text-bg-secondary"
356
364
  };
357
365
  //#endregion
358
366
  //#region src/components/StatusBadge.tsx
359
- function U({ status: e }) {
360
- let t = V[e] ?? "text-bg-secondary", n = B[e] ?? e;
367
+ function V({ status: e }) {
368
+ let t = z[e] ?? "text-bg-secondary", n = R[e] ?? e;
361
369
  return /* @__PURE__ */ p("span", {
362
370
  className: `badge rounded-pill ${t}`,
363
371
  children: n
@@ -365,7 +373,7 @@ function U({ status: e }) {
365
373
  }
366
374
  //#endregion
367
375
  //#region src/components/DatabaseBanner.tsx
368
- function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
376
+ function H({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
369
377
  if (t) return /* @__PURE__ */ p("div", {
370
378
  className: "mb-3",
371
379
  children: /* @__PURE__ */ m("small", {
@@ -384,7 +392,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
384
392
  "aria-hidden": !0
385
393
  }),
386
394
  "Não foi possível consultar o banco configurado: ",
387
- K(n)
395
+ W(n)
388
396
  ]
389
397
  });
390
398
  if (!e || !e.configured) return i ? /* @__PURE__ */ m("div", {
@@ -406,7 +414,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
406
414
  "). A criação e a importação usam esse banco."
407
415
  ]
408
416
  });
409
- let a = H[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
417
+ let a = B[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
410
418
  return i ? /* @__PURE__ */ m("div", {
411
419
  className: "mb-3",
412
420
  children: [
@@ -524,7 +532,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
524
532
  }
525
533
  //#endregion
526
534
  //#region src/components/RestoreModal.tsx
527
- function G({ backup: e, onClose: t }) {
535
+ function U({ backup: e, onClose: t }) {
528
536
  let n = A(), r = T(), i = () => {
529
537
  n.mutate({ id: e.id });
530
538
  }, a = n.isPending;
@@ -571,7 +579,7 @@ function G({ backup: e, onClose: t }) {
571
579
  " será aplicado no banco configurado abaixo."
572
580
  ]
573
581
  }),
574
- /* @__PURE__ */ p(W, {
582
+ /* @__PURE__ */ p(H, {
575
583
  label: "Banco de destino",
576
584
  dbInfo: r.data,
577
585
  isLoading: r.isLoading,
@@ -582,7 +590,7 @@ function G({ backup: e, onClose: t }) {
582
590
  children: [/* @__PURE__ */ p("i", {
583
591
  className: "bi bi-x-circle me-2",
584
592
  "aria-hidden": !0
585
- }), K(n.error)]
593
+ }), W(n.error)]
586
594
  }),
587
595
  n.isSuccess && /* @__PURE__ */ m("div", {
588
596
  className: "alert alert-success mt-3 mb-0",
@@ -617,12 +625,12 @@ function G({ backup: e, onClose: t }) {
617
625
  })
618
626
  })] });
619
627
  }
620
- function K(e) {
628
+ function W(e) {
621
629
  return e instanceof Error ? e.message : String(e);
622
630
  }
623
631
  //#endregion
624
632
  //#region src/components/BackupsTab.tsx
625
- function q() {
633
+ function G() {
626
634
  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();
627
635
  r(() => {
628
636
  if (!y.data || !y.variables) return;
@@ -665,7 +673,7 @@ function q() {
665
673
  "aria-hidden": !0
666
674
  }),
667
675
  "Não foi possível carregar os backups: ",
668
- J(g)
676
+ K(g)
669
677
  ]
670
678
  }),
671
679
  y.isError && /* @__PURE__ */ m("div", {
@@ -676,7 +684,7 @@ function q() {
676
684
  "aria-hidden": !0
677
685
  }),
678
686
  "Falha no download: ",
679
- J(y.error)
687
+ K(y.error)
680
688
  ]
681
689
  }),
682
690
  b.isError && /* @__PURE__ */ m("div", {
@@ -687,7 +695,7 @@ function q() {
687
695
  "aria-hidden": !0
688
696
  }),
689
697
  "Falha ao excluir: ",
690
- J(b.error)
698
+ K(b.error)
691
699
  ]
692
700
  }),
693
701
  x.isError && /* @__PURE__ */ m("div", {
@@ -698,7 +706,7 @@ function q() {
698
706
  "aria-hidden": !0
699
707
  }),
700
708
  "Falha ao verificar: ",
701
- J(x.error)
709
+ K(x.error)
702
710
  ]
703
711
  }),
704
712
  Object.entries(l).map(([e, t]) => /* @__PURE__ */ m("div", {
@@ -741,6 +749,10 @@ function q() {
741
749
  scope: "col",
742
750
  children: "Arquivo"
743
751
  }),
752
+ /* @__PURE__ */ p("th", {
753
+ scope: "col",
754
+ children: "Tipo"
755
+ }),
744
756
  /* @__PURE__ */ p("th", {
745
757
  scope: "col",
746
758
  children: "Banco"
@@ -772,7 +784,7 @@ function q() {
772
784
  children: "Ações"
773
785
  })
774
786
  ] }) }), /* @__PURE__ */ m("tbody", { children: [S.length === 0 && /* @__PURE__ */ p("tr", { children: /* @__PURE__ */ p("td", {
775
- colSpan: 9,
787
+ colSpan: 10,
776
788
  className: "text-center text-muted py-4",
777
789
  children: "Nenhum backup cadastrado."
778
790
  }) }), S.map((e) => /* @__PURE__ */ m("tr", { children: [
@@ -786,12 +798,19 @@ function q() {
786
798
  "aria-hidden": !0
787
799
  }), e.filename]
788
800
  }),
789
- /* @__PURE__ */ p("td", { children: H[e.databaseType] ?? e.databaseType }),
801
+ /* @__PURE__ */ p("td", { children: e.backupType ? /* @__PURE__ */ p("span", {
802
+ className: `badge ${re[e.backupType] ?? "text-bg-secondary"}`,
803
+ children: ne[e.backupType] ?? e.backupType
804
+ }) : /* @__PURE__ */ p("span", {
805
+ className: "text-muted",
806
+ children: "—"
807
+ }) }),
808
+ /* @__PURE__ */ p("td", { children: B[e.databaseType] ?? e.databaseType }),
790
809
  /* @__PURE__ */ p("td", {
791
810
  className: "text-end",
792
- children: R(e.fileSize)
811
+ children: I(e.fileSize)
793
812
  }),
794
- /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(U, { status: e.status }) }),
813
+ /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(V, { status: e.status }) }),
795
814
  /* @__PURE__ */ p("td", { children: e.sha256 ? /* @__PURE__ */ m("code", {
796
815
  title: e.sha256,
797
816
  style: { fontSize: "0.8em" },
@@ -814,7 +833,7 @@ function q() {
814
833
  className: "text-muted",
815
834
  children: "—"
816
835
  }) }),
817
- /* @__PURE__ */ p("td", { children: z(e.createdAt) }),
836
+ /* @__PURE__ */ p("td", { children: L(e.createdAt) }),
818
837
  /* @__PURE__ */ m("td", {
819
838
  className: "text-end text-nowrap",
820
839
  children: [
@@ -907,11 +926,11 @@ function q() {
907
926
  })]
908
927
  })]
909
928
  }),
910
- n && /* @__PURE__ */ p(G, {
929
+ n && /* @__PURE__ */ p(U, {
911
930
  backup: n,
912
931
  onClose: () => a(null)
913
932
  }),
914
- s && /* @__PURE__ */ p(ee, {
933
+ s && /* @__PURE__ */ p(ie, {
915
934
  backup: s,
916
935
  busy: b.isPending,
917
936
  onCancel: () => c(null),
@@ -921,7 +940,7 @@ function q() {
921
940
  })
922
941
  ] });
923
942
  }
924
- function ee(e) {
943
+ function ie(e) {
925
944
  let { backup: t, busy: n, onCancel: r, onConfirm: i } = e;
926
945
  return /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", { className: "modal-backdrop show" }), /* @__PURE__ */ p("div", {
927
946
  className: "modal show d-block",
@@ -987,16 +1006,13 @@ function ee(e) {
987
1006
  })
988
1007
  })] });
989
1008
  }
990
- function J(e) {
1009
+ function K(e) {
991
1010
  return e instanceof Error ? e.message : String(e);
992
1011
  }
993
1012
  //#endregion
994
1013
  //#region src/components/CreateBackupTab.tsx
995
- function Y() {
996
- let e = k(), t = T(), n = E(t.data?.configured), [r, a] = o(!1), [s, c] = o(() => ({
997
- type: "LOCAL",
998
- localPath: ""
999
- })), [l, u] = o(!0), [d, h] = o({
1014
+ function q() {
1015
+ let e = k(), t = T(), n = E(t.data?.configured), [r, a] = o(!0), [s, c] = o({
1000
1016
  format: "plain",
1001
1017
  includeData: !0,
1002
1018
  includeDropTable: !1,
@@ -1004,49 +1020,49 @@ function Y() {
1004
1020
  includeIndexes: !0,
1005
1021
  includeConstraints: !0,
1006
1022
  includeRelatedTables: !1
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(() => {
1008
- let e = n.data ?? [], t = S.trim().toLowerCase();
1023
+ }), [l, u] = o(() => /* @__PURE__ */ new Set()), [d, f] = o(null), [h, g] = o({}), [_, v] = o(""), y = D(d, t.data?.configured), b = i(() => new Set((t.data?.exportExcludedTables ?? []).map(oe)), [t.data?.exportExcludedTables]), x = i(() => {
1024
+ let e = n.data ?? [], t = _.trim().toLowerCase();
1009
1025
  return t ? e.filter((e) => e.fullName.toLowerCase().includes(t)) : e;
1010
- }, [n.data, S]), j = i(() => Object.values(b).reduce((e, t) => e + t.length, 0), [b]), M = (e) => {
1011
- _((t) => {
1026
+ }, [n.data, _]), S = i(() => Object.values(h).reduce((e, t) => e + t.length, 0), [h]), C = (e) => {
1027
+ u((t) => {
1012
1028
  let n = new Set(t);
1013
1029
  return n.has(e) ? n.delete(e) : n.add(e), n;
1014
1030
  });
1015
- }, N = (e, t) => {
1016
- x((n) => {
1031
+ }, w = (e, t) => {
1032
+ g((n) => {
1017
1033
  let r = n[e] ?? [], i = r.includes(t) ? r.filter((e) => e !== t) : [...r, t];
1018
1034
  return {
1019
1035
  ...n,
1020
1036
  [e]: i
1021
1037
  };
1022
1038
  });
1023
- }, P = () => {
1039
+ }, O = () => {
1024
1040
  let t;
1025
- if (l) {
1026
- t = { ...d }, g.size > 0 && (t.tables = [...g]);
1041
+ if (r) {
1042
+ t = { ...s }, l.size > 0 && (t.tables = [...l]);
1027
1043
  let e = {};
1028
- Object.entries(b).forEach(([t, n]) => {
1044
+ Object.entries(h).forEach(([t, n]) => {
1029
1045
  n.length > 0 && (e[t] = n);
1030
1046
  }), Object.keys(e).length > 0 && (t.excludeColumnsPerTable = e);
1031
1047
  }
1032
1048
  e.mutate({
1033
- originStorage: r ? s : void 0,
1049
+ backupType: "DATABASE",
1034
1050
  dumpOptions: t
1035
1051
  });
1036
- }, F = e.isPending, I = t.data?.configured && !F;
1052
+ }, A = e.isPending, j = t.data?.configured && !A;
1037
1053
  return /* @__PURE__ */ m("div", { children: [
1038
1054
  /* @__PURE__ */ m("div", {
1039
1055
  className: "mb-3",
1040
1056
  children: [/* @__PURE__ */ p("h5", {
1041
1057
  className: "mb-1",
1042
- children: "Criar novo backup"
1058
+ children: "Backup de banco de dados"
1043
1059
  }), /* @__PURE__ */ p("p", {
1044
1060
  className: "text-muted small mb-0",
1045
- children: "Selecione as tabelas no grid e expanda um card para marcar colunas cujo conteúdo será nulificado na exportação."
1061
+ children: "Selecione as tabelas no grid e expanda um card para marcar colunas cujo conteúdo será nulificado na exportação. Apenas o dump do banco é enviado ao storage de destino."
1046
1062
  })]
1047
1063
  }),
1048
- /* @__PURE__ */ p(W, {
1049
- label: "Banco de origem",
1064
+ /* @__PURE__ */ p(H, {
1065
+ label: "Banco de destino",
1050
1066
  dbInfo: t.data,
1051
1067
  isLoading: t.isLoading,
1052
1068
  error: t.error,
@@ -1064,7 +1080,7 @@ function Y() {
1064
1080
  "aria-hidden": !0
1065
1081
  }),
1066
1082
  "Tabelas do banco (",
1067
- n.data?.filter((e) => !O.has(e.fullName)).length ?? 0,
1083
+ n.data?.filter((e) => !b.has(e.fullName)).length ?? 0,
1068
1084
  " disponíveis)"
1069
1085
  ]
1070
1086
  }), /* @__PURE__ */ m("div", {
@@ -1072,16 +1088,16 @@ function Y() {
1072
1088
  children: [/* @__PURE__ */ p("input", {
1073
1089
  className: "form-control form-control-sm",
1074
1090
  placeholder: "Filtrar tabelas...",
1075
- value: S,
1076
- onChange: (e) => C(e.target.value),
1091
+ value: _,
1092
+ onChange: (e) => v(e.target.value),
1077
1093
  style: { width: 200 }
1078
- }), g.size > 0 && /* @__PURE__ */ m("button", {
1094
+ }), l.size > 0 && /* @__PURE__ */ m("button", {
1079
1095
  type: "button",
1080
1096
  className: "btn btn-sm btn-outline-secondary",
1081
- onClick: () => _(/* @__PURE__ */ new Set()),
1097
+ onClick: () => u(/* @__PURE__ */ new Set()),
1082
1098
  children: [
1083
1099
  "Limpar seleção (",
1084
- g.size,
1100
+ l.size,
1085
1101
  ")"
1086
1102
  ]
1087
1103
  })]
@@ -1104,10 +1120,10 @@ function Y() {
1104
1120
  "aria-hidden": !0
1105
1121
  }),
1106
1122
  "Falha ao listar tabelas: ",
1107
- K(n.error)
1123
+ W(n.error)
1108
1124
  ]
1109
1125
  }),
1110
- A.length === 0 && !n.isLoading && /* @__PURE__ */ p("div", {
1126
+ x.length === 0 && !n.isLoading && /* @__PURE__ */ p("div", {
1111
1127
  className: "p-3 text-muted small",
1112
1128
  children: "Nenhuma tabela encontrada."
1113
1129
  }),
@@ -1120,16 +1136,16 @@ function Y() {
1120
1136
  maxHeight: 420,
1121
1137
  overflow: "auto"
1122
1138
  },
1123
- children: A.map((e) => /* @__PURE__ */ p(te, {
1139
+ children: x.map((e) => /* @__PURE__ */ p(ae, {
1124
1140
  table: e,
1125
- disabled: O.has(e.fullName),
1126
- selected: g.has(e.fullName),
1127
- expanded: v === e.fullName,
1128
- excluded: b[e.fullName] ?? [],
1129
- onToggleTable: () => M(e.fullName),
1130
- onToggleExpand: () => y(v === e.fullName ? null : e.fullName),
1131
- onToggleColumn: (t) => N(e.fullName, t),
1132
- detail: v === e.fullName ? w : void 0
1141
+ disabled: b.has(e.fullName),
1142
+ selected: l.has(e.fullName),
1143
+ expanded: d === e.fullName,
1144
+ excluded: h[e.fullName] ?? [],
1145
+ onToggleTable: () => C(e.fullName),
1146
+ onToggleExpand: () => f(d === e.fullName ? null : e.fullName),
1147
+ onToggleColumn: (t) => w(e.fullName, t),
1148
+ detail: d === e.fullName ? y : void 0
1133
1149
  }, e.fullName))
1134
1150
  }),
1135
1151
  /* @__PURE__ */ m("div", {
@@ -1140,14 +1156,14 @@ function Y() {
1140
1156
  className: "bi bi-info-circle me-1",
1141
1157
  "aria-hidden": !0
1142
1158
  }), "Deixe tudo desmarcado para incluir todas as tabelas (exceto as bloqueadas por segurança)."]
1143
- }), j > 0 && d.format !== "plain" && /* @__PURE__ */ m("span", {
1159
+ }), S > 0 && s.format !== "plain" && /* @__PURE__ */ m("span", {
1144
1160
  className: "badge text-bg-warning ms-auto text-wrap text-start",
1145
1161
  children: [
1146
1162
  /* @__PURE__ */ p("i", {
1147
1163
  className: "bi bi-exclamation-triangle me-1",
1148
1164
  "aria-hidden": !0
1149
1165
  }),
1150
- j,
1166
+ S,
1151
1167
  " coluna(s) marcada(s) serão nulificadas apenas com formato Plain SQL (texto)"
1152
1168
  ]
1153
1169
  })]
@@ -1162,15 +1178,15 @@ function Y() {
1162
1178
  type: "checkbox",
1163
1179
  role: "switch",
1164
1180
  id: "minivault-with-dump-options",
1165
- checked: l,
1166
- onChange: (e) => u(e.target.checked)
1181
+ checked: r,
1182
+ onChange: (e) => a(e.target.checked)
1167
1183
  }), /* @__PURE__ */ p("label", {
1168
1184
  className: "form-check-label",
1169
1185
  htmlFor: "minivault-with-dump-options",
1170
1186
  children: "Configurar opcoes do dump (formato, dados, indices) — desmarcar envia sem opcoes (formato padrão custom/binary no servidor)"
1171
1187
  })]
1172
1188
  }),
1173
- l && /* @__PURE__ */ m("div", {
1189
+ r && /* @__PURE__ */ m("div", {
1174
1190
  className: "card mb-3",
1175
1191
  children: [/* @__PURE__ */ m("div", {
1176
1192
  className: "card-header d-flex align-items-center gap-2",
@@ -1191,8 +1207,8 @@ function Y() {
1191
1207
  }),
1192
1208
  /* @__PURE__ */ m("select", {
1193
1209
  className: "form-select",
1194
- value: d.format,
1195
- onChange: (e) => h((t) => ({
1210
+ value: s.format,
1211
+ onChange: (e) => c((t) => ({
1196
1212
  ...t,
1197
1213
  format: e.target.value
1198
1214
  })),
@@ -1258,8 +1274,8 @@ function Y() {
1258
1274
  className: "form-check-input",
1259
1275
  type: "checkbox",
1260
1276
  id: `dump-opt-${e}`,
1261
- checked: !!d[e],
1262
- onChange: (t) => h((n) => ({
1277
+ checked: !!s[e],
1278
+ onChange: (t) => c((n) => ({
1263
1279
  ...n,
1264
1280
  [e]: t.target.checked
1265
1281
  }))
@@ -1273,167 +1289,6 @@ function Y() {
1273
1289
  })]
1274
1290
  })]
1275
1291
  }),
1276
- /* @__PURE__ */ m("div", {
1277
- className: "form-check form-switch mb-3",
1278
- children: [/* @__PURE__ */ p("input", {
1279
- className: "form-check-input",
1280
- type: "checkbox",
1281
- role: "switch",
1282
- id: "minivault-with-origin",
1283
- checked: r,
1284
- onChange: (e) => a(e.target.checked)
1285
- }), /* @__PURE__ */ p("label", {
1286
- className: "form-check-label",
1287
- htmlFor: "minivault-with-origin",
1288
- children: "Incluir arquivos de origem no backup (armazenamento do sistema)"
1289
- })]
1290
- }),
1291
- r && /* @__PURE__ */ m("div", {
1292
- className: "card mb-3",
1293
- children: [/* @__PURE__ */ m("div", {
1294
- className: "card-header d-flex align-items-center gap-2",
1295
- children: [/* @__PURE__ */ p("i", {
1296
- className: "bi bi-folder2-open",
1297
- "aria-hidden": !0
1298
- }), "Origem dos arquivos"]
1299
- }), /* @__PURE__ */ p("div", {
1300
- className: "card-body",
1301
- children: /* @__PURE__ */ m("div", {
1302
- className: "row g-3",
1303
- children: [/* @__PURE__ */ m("div", {
1304
- className: "col-md-4",
1305
- children: [/* @__PURE__ */ p("label", {
1306
- className: "form-label",
1307
- children: "Tipo"
1308
- }), /* @__PURE__ */ m("select", {
1309
- className: "form-select",
1310
- value: s.type,
1311
- onChange: (e) => c((t) => ({
1312
- ...t,
1313
- type: e.target.value
1314
- })),
1315
- children: [/* @__PURE__ */ p("option", {
1316
- value: "LOCAL",
1317
- children: "Local (diretorio)"
1318
- }), /* @__PURE__ */ p("option", {
1319
- value: "S3",
1320
- children: "S3 (MinIO/S3)"
1321
- })]
1322
- })]
1323
- }), s.type === "LOCAL" ? /* @__PURE__ */ m("div", {
1324
- className: "col-md-8",
1325
- children: [/* @__PURE__ */ p("label", {
1326
- className: "form-label",
1327
- children: "Caminho local"
1328
- }), /* @__PURE__ */ p("input", {
1329
- className: "form-control",
1330
- value: s.localPath,
1331
- placeholder: "/dados/sistema",
1332
- onChange: (e) => c((t) => ({
1333
- ...t,
1334
- localPath: e.target.value
1335
- }))
1336
- })]
1337
- }) : /* @__PURE__ */ m(f, { children: [
1338
- /* @__PURE__ */ m("div", {
1339
- className: "col-md-6",
1340
- children: [/* @__PURE__ */ p("label", {
1341
- className: "form-label",
1342
- children: "Endpoint"
1343
- }), /* @__PURE__ */ p("input", {
1344
- className: "form-control",
1345
- value: s.endpoint,
1346
- placeholder: "http://192.168.0.10:9000",
1347
- onChange: (e) => c((t) => ({
1348
- ...t,
1349
- endpoint: e.target.value
1350
- }))
1351
- })]
1352
- }),
1353
- /* @__PURE__ */ m("div", {
1354
- className: "col-md-6",
1355
- children: [/* @__PURE__ */ p("label", {
1356
- className: "form-label",
1357
- children: "Bucket"
1358
- }), /* @__PURE__ */ p("input", {
1359
- className: "form-control",
1360
- value: s.bucketName,
1361
- placeholder: "storage",
1362
- onChange: (e) => c((t) => ({
1363
- ...t,
1364
- bucketName: e.target.value
1365
- }))
1366
- })]
1367
- }),
1368
- /* @__PURE__ */ m("div", {
1369
- className: "col-md-6",
1370
- children: [/* @__PURE__ */ p("label", {
1371
- className: "form-label",
1372
- children: "Access key"
1373
- }), /* @__PURE__ */ p("input", {
1374
- className: "form-control",
1375
- value: s.accessKey,
1376
- onChange: (e) => c((t) => ({
1377
- ...t,
1378
- accessKey: e.target.value
1379
- }))
1380
- })]
1381
- }),
1382
- /* @__PURE__ */ m("div", {
1383
- className: "col-md-6",
1384
- children: [/* @__PURE__ */ p("label", {
1385
- className: "form-label",
1386
- children: "Secret key"
1387
- }), /* @__PURE__ */ p("input", {
1388
- className: "form-control",
1389
- type: "password",
1390
- value: s.secretKey,
1391
- onChange: (e) => c((t) => ({
1392
- ...t,
1393
- secretKey: e.target.value
1394
- }))
1395
- })]
1396
- }),
1397
- /* @__PURE__ */ m("div", {
1398
- className: "col-md-6",
1399
- children: [/* @__PURE__ */ p("label", {
1400
- className: "form-label",
1401
- children: "Regiao"
1402
- }), /* @__PURE__ */ p("input", {
1403
- className: "form-control",
1404
- value: s.region,
1405
- placeholder: "us-east-1",
1406
- onChange: (e) => c((t) => ({
1407
- ...t,
1408
- region: e.target.value
1409
- }))
1410
- })]
1411
- }),
1412
- /* @__PURE__ */ p("div", {
1413
- className: "col-md-6 d-flex align-items-end pb-1",
1414
- children: /* @__PURE__ */ m("div", {
1415
- className: "form-check form-switch",
1416
- children: [/* @__PURE__ */ p("input", {
1417
- className: "form-check-input",
1418
- type: "checkbox",
1419
- role: "switch",
1420
- id: "minivault-origin-pathstyle",
1421
- checked: !!s.pathStyleEnabled,
1422
- onChange: (e) => c((t) => ({
1423
- ...t,
1424
- pathStyleEnabled: e.target.checked
1425
- }))
1426
- }), /* @__PURE__ */ p("label", {
1427
- className: "form-check-label",
1428
- htmlFor: "minivault-origin-pathstyle",
1429
- children: "Path-style (MinIO)"
1430
- })]
1431
- })
1432
- })
1433
- ] })]
1434
- })
1435
- })]
1436
- }),
1437
1292
  e.isError && /* @__PURE__ */ m("div", {
1438
1293
  className: "alert alert-danger",
1439
1294
  children: [
@@ -1442,7 +1297,7 @@ function Y() {
1442
1297
  "aria-hidden": !0
1443
1298
  }),
1444
1299
  "Falha ao criar backup: ",
1445
- K(e.error)
1300
+ W(e.error)
1446
1301
  ]
1447
1302
  }),
1448
1303
  e.isSuccess && /* @__PURE__ */ m("div", {
@@ -1464,10 +1319,10 @@ function Y() {
1464
1319
  children: /* @__PURE__ */ m("button", {
1465
1320
  type: "button",
1466
1321
  className: "btn btn-primary",
1467
- onClick: P,
1468
- disabled: !I,
1322
+ onClick: O,
1323
+ disabled: !j,
1469
1324
  children: [
1470
- F && /* @__PURE__ */ p("span", {
1325
+ A && /* @__PURE__ */ p("span", {
1471
1326
  className: "spinner-border spinner-border-sm me-2",
1472
1327
  "aria-hidden": !0
1473
1328
  }),
@@ -1475,13 +1330,13 @@ function Y() {
1475
1330
  className: "bi bi-plus-lg me-1",
1476
1331
  "aria-hidden": !0
1477
1332
  }),
1478
- "Iniciar backup"
1333
+ "Iniciar backup de banco"
1479
1334
  ]
1480
1335
  })
1481
1336
  })
1482
1337
  ] });
1483
1338
  }
1484
- function te({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1339
+ function ae({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1485
1340
  return /* @__PURE__ */ p("div", {
1486
1341
  className: `card h-100 ${n ? "border-primary" : ""}`,
1487
1342
  style: { minWidth: 0 },
@@ -1545,7 +1400,7 @@ function te({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1545
1400
  }),
1546
1401
  a?.isError && /* @__PURE__ */ m("small", {
1547
1402
  className: "text-danger",
1548
- children: ["Erro: ", K(a.error)]
1403
+ children: ["Erro: ", W(a.error)]
1549
1404
  }),
1550
1405
  a?.data && /* @__PURE__ */ m(f, { children: [a.data.columns.map((e) => /* @__PURE__ */ m("div", {
1551
1406
  className: "d-flex align-items-center gap-2 py-1",
@@ -1596,13 +1451,13 @@ function te({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1596
1451
  })
1597
1452
  });
1598
1453
  }
1599
- function ne(e) {
1454
+ function oe(e) {
1600
1455
  let t = e.trim();
1601
1456
  return t.includes(".") ? t : `public.${t}`;
1602
1457
  }
1603
1458
  //#endregion
1604
1459
  //#region src/components/DiagnosticsTab.tsx
1605
- function X() {
1460
+ function J() {
1606
1461
  let { data: e, isLoading: t, isError: n, error: r, refetch: a, isFetching: o } = C(), s = i(() => e ? {
1607
1462
  total: e.length,
1608
1463
  okCount: e.filter((e) => e.ok).length,
@@ -1643,7 +1498,7 @@ function X() {
1643
1498
  "aria-hidden": !0
1644
1499
  }),
1645
1500
  "Não foi possível executar as checagens: ",
1646
- K(r)
1501
+ W(r)
1647
1502
  ]
1648
1503
  }),
1649
1504
  s && /* @__PURE__ */ m("div", {
@@ -1680,7 +1535,7 @@ function X() {
1680
1535
  className: "card",
1681
1536
  children: /* @__PURE__ */ m("ul", {
1682
1537
  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", {
1538
+ children: [(e ?? []).map((e) => /* @__PURE__ */ p(se, { check: e }, e.name)), e && e.length === 0 && /* @__PURE__ */ p("li", {
1684
1539
  className: "list-group-item text-center text-muted py-4",
1685
1540
  children: "Nenhuma checagem retornada."
1686
1541
  })]
@@ -1688,7 +1543,7 @@ function X() {
1688
1543
  })
1689
1544
  ] });
1690
1545
  }
1691
- function re({ check: e }) {
1546
+ function se({ check: e }) {
1692
1547
  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
1548
  return /* @__PURE__ */ m("li", {
1694
1549
  className: "list-group-item d-flex align-items-start gap-3",
@@ -1724,7 +1579,7 @@ function re({ check: e }) {
1724
1579
  }
1725
1580
  //#endregion
1726
1581
  //#region src/components/StorageExplorerTab.tsx
1727
- function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1582
+ function Y({ storageType: e = "LOCAL", storageConfig: n }) {
1728
1583
  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({
1729
1584
  queryKey: [
1730
1585
  "storage-files",
@@ -1761,7 +1616,7 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1761
1616
  } catch {}
1762
1617
  }, N = async () => {
1763
1618
  for (let e of v) await M(e);
1764
- }, P = (e) => e ? z(e) : "-";
1619
+ }, P = (e) => e ? L(e) : "-";
1765
1620
  return /* @__PURE__ */ m("div", {
1766
1621
  className: "space-y-3",
1767
1622
  children: [
@@ -1891,7 +1746,7 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1891
1746
  }) }),
1892
1747
  /* @__PURE__ */ p("td", {
1893
1748
  className: "text-muted small",
1894
- children: e.directory ? "-" : R(e.size)
1749
+ children: e.directory ? "-" : I(e.size)
1895
1750
  }),
1896
1751
  /* @__PURE__ */ p("td", {
1897
1752
  className: "text-muted small",
@@ -1996,7 +1851,7 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1996
1851
  }
1997
1852
  //#endregion
1998
1853
  //#region src/components/ImportDumpTab.tsx
1999
- function Q() {
1854
+ function X() {
2000
1855
  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 = () => {
2001
1856
  i === "upload" && c ? e.mutate({ file: c }) : i === "storage" && u && t.mutate({
2002
1857
  storageType: "LOCAL",
@@ -2119,7 +1974,7 @@ function Q() {
2119
1974
  maxHeight: 350,
2120
1975
  overflow: "auto"
2121
1976
  },
2122
- children: /* @__PURE__ */ p(Z, {
1977
+ children: /* @__PURE__ */ p(Y, {
2123
1978
  storageType: "LOCAL",
2124
1979
  storageConfig: {
2125
1980
  type: "LOCAL",
@@ -2142,7 +1997,7 @@ function Q() {
2142
1997
  children: [/* @__PURE__ */ p("p", {
2143
1998
  className: "small text-body-secondary mb-3",
2144
1999
  children: "O dump será aplicado no banco configurado no servidor. Tabelas bloqueadas por segurança são ignoradas automaticamente na importação."
2145
- }), /* @__PURE__ */ p(W, {
2000
+ }), /* @__PURE__ */ p(H, {
2146
2001
  label: "Banco de destino",
2147
2002
  dbInfo: n.data,
2148
2003
  isLoading: n.isLoading,
@@ -2158,7 +2013,7 @@ function Q() {
2158
2013
  "aria-hidden": !0
2159
2014
  }),
2160
2015
  "Falha na importacao: ",
2161
- K(y)
2016
+ W(y)
2162
2017
  ]
2163
2018
  }),
2164
2019
  v && /* @__PURE__ */ m("div", {
@@ -2199,11 +2054,139 @@ function Q() {
2199
2054
  ] });
2200
2055
  }
2201
2056
  //#endregion
2057
+ //#region src/components/StorageBackupTab.tsx
2058
+ function Z() {
2059
+ let e = k(), t = C(), n = t.data?.find((e) => e.name === "ORIGIN_STORAGE"), r = t.data?.find((e) => e.name === "OUTPUT_STORAGE"), i = e.isPending;
2060
+ return /* @__PURE__ */ m("div", { children: [
2061
+ /* @__PURE__ */ m("div", {
2062
+ className: "mb-3",
2063
+ children: [/* @__PURE__ */ p("h5", {
2064
+ className: "mb-1",
2065
+ children: "Backup de storage"
2066
+ }), /* @__PURE__ */ m("p", {
2067
+ className: "text-muted small mb-0",
2068
+ children: [
2069
+ "Faz backup dos arquivos do storage de origem para o storage de destino. Origem e destino são configurados no servidor via variáveis de ambiente (",
2070
+ /* @__PURE__ */ p("code", { children: "ORIGIN_STORAGE_*" }),
2071
+ " e ",
2072
+ /* @__PURE__ */ p("code", { children: "OUTPUT_STORAGE_*" }),
2073
+ ")."
2074
+ ]
2075
+ })]
2076
+ }),
2077
+ /* @__PURE__ */ m("div", {
2078
+ className: "card mb-3",
2079
+ children: [/* @__PURE__ */ m("div", {
2080
+ className: "card-header d-flex align-items-center gap-2",
2081
+ children: [/* @__PURE__ */ p("i", {
2082
+ className: "bi bi-heart-pulse",
2083
+ "aria-hidden": !0
2084
+ }), "Status das conexões"]
2085
+ }), /* @__PURE__ */ m("div", {
2086
+ className: "card-body",
2087
+ children: [
2088
+ t.isLoading && /* @__PURE__ */ m("div", {
2089
+ className: "text-muted small",
2090
+ children: [/* @__PURE__ */ p("span", {
2091
+ className: "spinner-border spinner-border-sm me-2",
2092
+ "aria-hidden": !0
2093
+ }), "Verificando conexões..."]
2094
+ }),
2095
+ t.isError && /* @__PURE__ */ p("div", {
2096
+ className: "text-muted small",
2097
+ children: "Não foi possível verificar o status. O backup pode ser executado mesmo assim."
2098
+ }),
2099
+ t.data && /* @__PURE__ */ m("div", {
2100
+ className: "d-flex flex-column gap-2",
2101
+ children: [/* @__PURE__ */ p(Q, {
2102
+ label: n?.label ?? "Storage de origem",
2103
+ configured: n?.configured,
2104
+ ok: n?.ok,
2105
+ message: n?.message
2106
+ }), /* @__PURE__ */ p(Q, {
2107
+ label: r?.label ?? "Storage de destino",
2108
+ configured: r?.configured,
2109
+ ok: r?.ok,
2110
+ message: r?.message
2111
+ })]
2112
+ })
2113
+ ]
2114
+ })]
2115
+ }),
2116
+ e.isError && /* @__PURE__ */ m("div", {
2117
+ className: "alert alert-danger",
2118
+ children: [
2119
+ /* @__PURE__ */ p("i", {
2120
+ className: "bi bi-x-circle me-2",
2121
+ "aria-hidden": !0
2122
+ }),
2123
+ "Falha ao criar backup de storage: ",
2124
+ W(e.error)
2125
+ ]
2126
+ }),
2127
+ e.isSuccess && /* @__PURE__ */ m("div", {
2128
+ className: "alert alert-success",
2129
+ children: [
2130
+ /* @__PURE__ */ p("i", {
2131
+ className: "bi bi-check-circle me-2",
2132
+ "aria-hidden": !0
2133
+ }),
2134
+ "Backup de storage #",
2135
+ e.data.id,
2136
+ " criado (",
2137
+ e.data.filename,
2138
+ ")."
2139
+ ]
2140
+ }),
2141
+ /* @__PURE__ */ p("div", {
2142
+ className: "d-flex justify-content-end",
2143
+ children: /* @__PURE__ */ m("button", {
2144
+ type: "button",
2145
+ className: "btn btn-primary",
2146
+ onClick: () => {
2147
+ e.mutate({ backupType: "STORAGE" });
2148
+ },
2149
+ disabled: i || t.data != null && n?.configured === !1,
2150
+ children: [
2151
+ i && /* @__PURE__ */ p("span", {
2152
+ className: "spinner-border spinner-border-sm me-2",
2153
+ "aria-hidden": !0
2154
+ }),
2155
+ /* @__PURE__ */ p("i", {
2156
+ className: "bi bi-plus-lg me-1",
2157
+ "aria-hidden": !0
2158
+ }),
2159
+ "Iniciar backup de storage"
2160
+ ]
2161
+ })
2162
+ })
2163
+ ] });
2164
+ }
2165
+ function Q({ label: e, configured: t, ok: n, message: r }) {
2166
+ return t === !1 ? /* @__PURE__ */ m("div", {
2167
+ className: "d-flex align-items-center gap-2 small text-muted",
2168
+ children: [/* @__PURE__ */ p("i", {
2169
+ className: "bi bi-dash-circle text-secondary",
2170
+ "aria-hidden": !0
2171
+ }), /* @__PURE__ */ m("span", { children: [/* @__PURE__ */ m("strong", { children: [e, ":"] }), " não configurado no servidor"] })]
2172
+ }) : /* @__PURE__ */ m("div", {
2173
+ className: `d-flex align-items-center gap-2 small ${n ? "text-success" : "text-danger"}`,
2174
+ children: [/* @__PURE__ */ p("i", {
2175
+ className: `bi ${n ? "bi-check-circle-fill" : "bi-x-circle-fill"}`,
2176
+ "aria-hidden": !0
2177
+ }), /* @__PURE__ */ m("span", { children: [
2178
+ /* @__PURE__ */ m("strong", { children: [e, ":"] }),
2179
+ " ",
2180
+ r ?? (n ? "ok" : "falha")
2181
+ ] })]
2182
+ });
2183
+ }
2184
+ //#endregion
2202
2185
  //#region src/hooks/useAutoTheme.ts
2203
2186
  function $() {
2204
2187
  return typeof document < "u" && document.documentElement.classList.contains("dark");
2205
2188
  }
2206
- function ie(e) {
2189
+ function ce(e) {
2207
2190
  let [t, n] = o($), [i, a] = o(() => typeof window < "u" && window.matchMedia?.("(prefers-color-scheme: dark)")?.matches === !0);
2208
2191
  return r(() => {
2209
2192
  let e = document.documentElement, t = new MutationObserver(() => n($()));
@@ -2219,8 +2202,8 @@ function ie(e) {
2219
2202
  }
2220
2203
  //#endregion
2221
2204
  //#region src/components/MinivaultModule.tsx
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: {
2205
+ function le(e) {
2206
+ let { companyAcronym: t = "Minivault", theme: n, refetchIntervalMs: r = 15e3, storageType: i = "LOCAL", ...a } = e, l = ce(n), [u] = o(() => new s({ defaultOptions: { queries: {
2224
2207
  refetchInterval: r,
2225
2208
  retry: 1
2226
2209
  } } }));
@@ -2231,15 +2214,15 @@ function ae(e) {
2231
2214
  children: /* @__PURE__ */ m("div", {
2232
2215
  className: "minivault-module h-full w-full overflow-auto",
2233
2216
  "data-minivault-theme": l,
2234
- children: [/* @__PURE__ */ p(oe, {
2217
+ children: [/* @__PURE__ */ p(ue, {
2235
2218
  companyAcronym: t,
2236
2219
  theme: l
2237
- }), /* @__PURE__ */ p(se, { storageType: i })]
2220
+ }), /* @__PURE__ */ p(de, { storageType: i })]
2238
2221
  })
2239
2222
  })
2240
2223
  });
2241
2224
  }
2242
- function oe({ companyAcronym: e, theme: t }) {
2225
+ function ue({ companyAcronym: e, theme: t }) {
2243
2226
  return /* @__PURE__ */ m("div", {
2244
2227
  className: "d-flex align-items-center justify-content-between flex-wrap gap-2 px-3 py-2 border-bottom",
2245
2228
  style: {
@@ -2257,7 +2240,7 @@ function oe({ companyAcronym: e, theme: t }) {
2257
2240
  })
2258
2241
  }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("strong", { children: [e, " · Backups"] }), /* @__PURE__ */ p("div", {
2259
2242
  className: "small text-muted",
2260
- children: "Gerenciamento de backups de banco de dados"
2243
+ children: "Gerenciamento de backups de banco de dados e storage"
2261
2244
  })] })]
2262
2245
  }), /* @__PURE__ */ p("div", {
2263
2246
  className: "d-flex align-items-center gap-2",
@@ -2268,7 +2251,7 @@ function oe({ companyAcronym: e, theme: t }) {
2268
2251
  })]
2269
2252
  });
2270
2253
  }
2271
- function se({ storageType: e }) {
2254
+ function de({ storageType: e }) {
2272
2255
  let [t, n] = o("backups");
2273
2256
  return /* @__PURE__ */ m("div", {
2274
2257
  className: "p-3",
@@ -2297,7 +2280,19 @@ function se({ storageType: e }) {
2297
2280
  children: [/* @__PURE__ */ p("i", {
2298
2281
  className: "bi bi-plus-circle me-1",
2299
2282
  "aria-hidden": !0
2300
- }), "Novo backup"]
2283
+ }), "Backup de BD"]
2284
+ })
2285
+ }),
2286
+ /* @__PURE__ */ p("li", {
2287
+ className: "nav-item",
2288
+ children: /* @__PURE__ */ m("button", {
2289
+ type: "button",
2290
+ className: `nav-link ${t === "createStorage" ? "active" : ""}`,
2291
+ onClick: () => n("createStorage"),
2292
+ children: [/* @__PURE__ */ p("i", {
2293
+ className: "bi bi-plus-circle me-1",
2294
+ "aria-hidden": !0
2295
+ }), "Backup de storage"]
2301
2296
  })
2302
2297
  }),
2303
2298
  /* @__PURE__ */ p("li", {
@@ -2338,22 +2333,23 @@ function se({ storageType: e }) {
2338
2333
  })
2339
2334
  ]
2340
2335
  }),
2341
- t === "backups" && /* @__PURE__ */ p(q, {}),
2342
- t === "create" && /* @__PURE__ */ p(Y, {}),
2343
- t === "import" && /* @__PURE__ */ p(Q, {}),
2344
- t === "storage" && /* @__PURE__ */ p(Z, { storageType: e }),
2345
- t === "diagnostics" && /* @__PURE__ */ p(X, {})
2336
+ t === "backups" && /* @__PURE__ */ p(G, {}),
2337
+ t === "create" && /* @__PURE__ */ p(q, {}),
2338
+ t === "createStorage" && /* @__PURE__ */ p(Z, {}),
2339
+ t === "import" && /* @__PURE__ */ p(X, {}),
2340
+ t === "storage" && /* @__PURE__ */ p(Y, { storageType: e }),
2341
+ t === "diagnostics" && /* @__PURE__ */ p(J, {})
2346
2342
  ]
2347
2343
  });
2348
2344
  }
2349
2345
  //#endregion
2350
2346
  //#region src/components/CredentialsFields.tsx
2351
- var ce = [
2347
+ var fe = [
2352
2348
  "postgresql",
2353
2349
  "mysql",
2354
2350
  "sqlserver"
2355
2351
  ];
2356
- function le() {
2352
+ function pe() {
2357
2353
  return {
2358
2354
  host: "localhost",
2359
2355
  port: 5432,
@@ -2363,19 +2359,19 @@ function le() {
2363
2359
  databaseType: "postgresql"
2364
2360
  };
2365
2361
  }
2366
- function ue(e) {
2362
+ function me(e) {
2367
2363
  return e === "postgresql" || e === "postgres" ? 5432 : e === "mysql" ? 3306 : 1433;
2368
2364
  }
2369
- function de({ value: e, onChange: t, onValidatedChange: n }) {
2365
+ function he({ value: e, onChange: t, onValidatedChange: n }) {
2370
2366
  let r = {
2371
- ...le(),
2367
+ ...pe(),
2372
2368
  ...e
2373
2369
  }, i = (e) => {
2374
2370
  let n = {
2375
2371
  ...r,
2376
2372
  databaseType: e
2377
2373
  };
2378
- (r.port === ue(r.databaseType) || !r.port) && (n.port = ue(e)), t(n);
2374
+ (r.port === me(r.databaseType) || !r.port) && (n.port = me(e)), t(n);
2379
2375
  }, a = (e) => {
2380
2376
  let i = {
2381
2377
  ...r,
@@ -2397,9 +2393,9 @@ function de({ value: e, onChange: t, onValidatedChange: n }) {
2397
2393
  className: "form-select",
2398
2394
  value: r.databaseType,
2399
2395
  onChange: (e) => i(e.target.value),
2400
- children: ce.map((e) => /* @__PURE__ */ p("option", {
2396
+ children: fe.map((e) => /* @__PURE__ */ p("option", {
2401
2397
  value: e,
2402
- children: H[e] ?? e
2398
+ children: B[e] ?? e
2403
2399
  }, e))
2404
2400
  })]
2405
2401
  }),
@@ -2471,4 +2467,4 @@ function de({ value: e, onChange: t, onValidatedChange: n }) {
2471
2467
  });
2472
2468
  }
2473
2469
  //#endregion
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 };
2470
+ export { G as BackupsTab, q as CreateBackupTab, he as CredentialsFields, B as DATABASE_TYPE_LABELS, H as DatabaseBanner, J as DiagnosticsTab, X as ImportDumpTab, g as MinivaultApi, h as MinivaultApiError, y as MinivaultApiProvider, le as MinivaultModule, U as RestoreModal, z as STATUS_BADGE, R as STATUS_LABELS, V as StatusBadge, Z as StorageBackupTab, Y as StorageExplorerTab, L as formatDate, I 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 };
@@ -1,4 +1,5 @@
1
1
  export type BackupStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'RESTORING' | 'RESTORED' | 'RESTORE_FAILED';
2
+ export type BackupType = 'DATABASE' | 'STORAGE' | 'FULL';
2
3
  export type DatabaseType = 'postgresql' | 'postgres' | 'mysql' | 'sqlserver' | 'mssql';
3
4
  export type DumpFormat = 'custom' | 'plain' | 'tar';
4
5
  export interface Backup {
@@ -7,6 +8,8 @@ export interface Backup {
7
8
  fileSize: number | null;
8
9
  status: BackupStatus;
9
10
  databaseType: string;
11
+ backupType: BackupType | null;
12
+ filePath: string | null;
10
13
  sha256: string | null;
11
14
  retentionDays: number | null;
12
15
  errorMessage: string | null;
@@ -67,6 +70,8 @@ export interface DumpOptions {
67
70
  includeRelatedTables?: boolean;
68
71
  }
69
72
  export interface BackupRequest {
73
+ /** Tipo do backup: DATABASE (apenas banco), STORAGE (apenas arquivos) ou FULL (padrão legado). */
74
+ backupType?: BackupType;
70
75
  /** Opcional: usa o banco padrão configurado no servidor (env) quando ausente. */
71
76
  database?: DatabaseCredentials;
72
77
  originStorage?: OriginStorageConfig;
@@ -1,6 +1,8 @@
1
- import { BackupStatus } from '../types/minivault';
1
+ import { BackupStatus, BackupType } from '../types/minivault';
2
2
  export declare function formatFileSize(bytes: number | null | undefined): string;
3
3
  export declare function formatDate(value: string | null | undefined): string;
4
4
  export declare const STATUS_LABELS: Record<BackupStatus, string>;
5
5
  export declare const STATUS_BADGE: Record<BackupStatus, string>;
6
6
  export declare const DATABASE_TYPE_LABELS: Record<string, string>;
7
+ export declare const BACKUP_TYPE_LABELS: Record<BackupType, string>;
8
+ export declare const BACKUP_TYPE_BADGE: Record<BackupType, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@v1ct0rbr/minivault-web",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
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
5
  "author": "Victor Queiroga (https://www.linkedin.com/in/victor-queiroga)",
6
6
  "license": "MIT",