@v1ct0rbr/minivault-web 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { Backup, BackupType, ConnectionCheck, DatabaseCredentials, DbInfo, DumpOptions, ImportResult, OriginStorageConfig, PageResponse, RelationshipInfo, StorageFileItem, TableDetail, TableInfo, VerifyResult } from '../types/minivault';
1
+ import { AllStorageConfig, Backup, BackupType, ConnectionCheck, DatabaseCredentials, DbInfo, DumpOptions, ImportResult, OriginStorageConfig, PageResponse, RelationshipInfo, StorageFileItem, StorageTarget, StorageZipRequest, TableDetail, TableInfo, VerifyResult, ZipJobStatus } from '../types/minivault';
2
2
  export interface MinivaultApiOptions {
3
3
  /**
4
4
  * URL base da API. Exemplos:
@@ -45,7 +45,12 @@ export declare class MinivaultApi {
45
45
  getDbTable(ref: string): Promise<TableDetail>;
46
46
  listDbRelationships(): Promise<RelationshipInfo[]>;
47
47
  getDiagnostics(): Promise<ConnectionCheck[]>;
48
- listStorageFiles(type: string, prefix?: string, storageConfig?: OriginStorageConfig): Promise<StorageFileItem[]>;
49
- downloadStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig): Promise<Blob>;
50
- deleteStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig): Promise<void>;
48
+ getStorageConfig(): Promise<AllStorageConfig>;
49
+ private storageQuery;
50
+ listStorageFiles(type: string, prefix?: string, storageConfig?: OriginStorageConfig, storage?: StorageTarget): Promise<StorageFileItem[]>;
51
+ downloadStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig, storage?: StorageTarget): Promise<Blob>;
52
+ deleteStorageFile(key: string, type: string, storageConfig?: OriginStorageConfig, storage?: StorageTarget): Promise<void>;
53
+ createStorageZip(request: StorageZipRequest): Promise<ZipJobStatus>;
54
+ getStorageZipStatus(jobId: string): Promise<ZipJobStatus>;
55
+ downloadStorageZip(jobId: string): Promise<Blob>;
51
56
  }
@@ -1,7 +1,10 @@
1
- import { OriginStorageConfig } from '../types/minivault';
1
+ import { OriginStorageConfig, StorageTarget } from '../types/minivault';
2
2
  interface StorageExplorerTabProps {
3
+ /** Qual storage o explorador manipula (origem ou destino). */
4
+ target?: StorageTarget;
5
+ title?: string;
3
6
  storageType?: string;
4
7
  storageConfig?: OriginStorageConfig;
5
8
  }
6
- export declare function StorageExplorerTab({ storageType, storageConfig }: StorageExplorerTabProps): import("react").JSX.Element;
9
+ export declare function StorageExplorerTab({ target, title, storageType, storageConfig, }: StorageExplorerTabProps): import("react").JSX.Element;
7
10
  export {};
package/dist/index.d.ts CHANGED
@@ -15,4 +15,4 @@ export { DatabaseBanner } from './components/DatabaseBanner';
15
15
  export { StatusBadge } from './components/StatusBadge';
16
16
  export { useBackups, useCreateBackup, useRestoreBackup, useDeleteBackup, useDownloadBackup, useImportDump, useImportFromStorage, useDbInfo, useDbTables, useDbTableDetail, useDbRelationships, useDiagnostics, } from './hooks/useBackups';
17
17
  export { formatFileSize, formatDate, STATUS_LABELS, STATUS_BADGE, DATABASE_TYPE_LABELS } from './utils/format';
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';
18
+ export type { AllStorageConfig, Backup, BackupRequest, BackupStatus, BackupType, ColumnInfo, ConnectionCheck, DatabaseCredentials, DatabaseType, DbInfo, DumpOptions, DumpFormat, ImportResult, ImportFromStorageRequest, OriginStorageConfig, PageResponse, RelationshipInfo, ResolvedStorageInfo, RestoreRequest, StorageFileItem, StorageTarget, TableDetail, TableInfo, VerifyResult, } from './types/minivault';
@@ -138,37 +138,63 @@ var h = class extends Error {
138
138
  getDiagnostics() {
139
139
  return this.request("/api/diagnostics");
140
140
  }
141
- listStorageFiles(e, t, n) {
142
- let r = new URLSearchParams({ type: e });
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()}`);
141
+ getStorageConfig() {
142
+ return this.request("/api/storage/config");
144
143
  }
145
- async downloadStorageFile(e, t, n) {
146
- let r = await this.resolveToken(), i = new URLSearchParams({
147
- key: e,
148
- type: t
149
- });
150
- n && (n.endpoint && i.set("endpoint", n.endpoint), n.bucketName && i.set("bucketName", n.bucketName), n.accessKey && i.set("accessKey", n.accessKey), n.secretKey && i.set("secretKey", n.secretKey), n.region && i.set("region", n.region), n.pathStyleEnabled && i.set("pathStyleEnabled", "true"), n.localPath && i.set("localPath", n.localPath));
151
- let a = { Accept: "application/octet-stream" };
152
- r && (a.Authorization = `Bearer ${r}`);
153
- let o = new AbortController(), s = setTimeout(() => o.abort(), this.timeoutMs);
144
+ storageQuery(e, t) {
145
+ let n = new URLSearchParams({ storage: e });
146
+ return t && (t.type && n.set("type", t.type), t.endpoint && n.set("endpoint", t.endpoint), t.bucketName && n.set("bucketName", t.bucketName), t.accessKey && n.set("accessKey", t.accessKey), t.secretKey && n.set("secretKey", t.secretKey), t.region && n.set("region", t.region), t.pathStyleEnabled && n.set("pathStyleEnabled", "true"), t.localPath && n.set("localPath", t.localPath)), n;
147
+ }
148
+ listStorageFiles(e, t, n, r = "origin") {
149
+ let i = this.storageQuery(r, n);
150
+ return i.set("type", e), t && i.set("prefix", t), this.request(`/api/storage/files?${i.toString()}`);
151
+ }
152
+ async downloadStorageFile(e, t, n, r = "origin") {
153
+ let i = await this.resolveToken(), a = this.storageQuery(r, n);
154
+ a.set("key", e), a.set("type", t);
155
+ let o = { Accept: "application/octet-stream" };
156
+ i && (o.Authorization = `Bearer ${i}`);
157
+ let s = new AbortController(), c = setTimeout(() => s.abort(), this.timeoutMs);
154
158
  try {
155
- let e = await this.fetchFn(`${this.baseUrl}/api/storage/files/download?${i.toString()}`, {
159
+ let e = await this.fetchFn(`${this.baseUrl}/api/storage/files/download?${a.toString()}`, {
156
160
  method: "GET",
157
- headers: a,
158
- signal: o.signal
161
+ headers: o,
162
+ signal: s.signal
159
163
  });
160
164
  if (!e.ok) throw new h(`Minivault API GET /api/storage/files/download falhou (HTTP ${e.status})`, e.status);
161
165
  return await e.blob();
162
166
  } finally {
163
- clearTimeout(s);
167
+ clearTimeout(c);
164
168
  }
165
169
  }
166
- async deleteStorageFile(e, t, n) {
167
- let r = new URLSearchParams({
168
- key: e,
169
- type: t
170
+ async deleteStorageFile(e, t, n, r = "origin") {
171
+ let i = this.storageQuery(r, n);
172
+ i.set("key", e), i.set("type", t), await this.request(`/api/storage/files?${i.toString()}`, { method: "DELETE" });
173
+ }
174
+ createStorageZip(e) {
175
+ return this.request("/api/storage/download/zip", {
176
+ method: "POST",
177
+ body: JSON.stringify(e)
170
178
  });
171
- 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)), await this.request(`/api/storage/files?${r.toString()}`, { method: "DELETE" });
179
+ }
180
+ getStorageZipStatus(e) {
181
+ return this.request(`/api/storage/download/zip/${encodeURIComponent(e)}`);
182
+ }
183
+ async downloadStorageZip(e) {
184
+ let t = await this.resolveToken(), n = { Accept: "application/octet-stream" };
185
+ t && (n.Authorization = `Bearer ${t}`);
186
+ let r = new AbortController(), i = setTimeout(() => r.abort(), this.timeoutMs);
187
+ try {
188
+ let t = await this.fetchFn(`${this.baseUrl}/api/storage/download/zip/${encodeURIComponent(e)}/content`, {
189
+ method: "GET",
190
+ headers: n,
191
+ signal: r.signal
192
+ });
193
+ if (!t.ok) throw new h(`Minivault API GET /api/storage/download/zip/${e}/content falhou (HTTP ${t.status})`, t.status);
194
+ return await t.blob();
195
+ } finally {
196
+ clearTimeout(i);
197
+ }
172
198
  }
173
199
  };
174
200
  function _(e) {
@@ -297,46 +323,46 @@ function P() {
297
323
  onSuccess: () => t.invalidateQueries({ queryKey: S.all })
298
324
  });
299
325
  }
300
- function ee() {
326
+ function F() {
301
327
  let e = b();
302
328
  return l({ mutationFn: (t) => e.downloadBackup(t.id) });
303
329
  }
304
- function F() {
330
+ function I() {
305
331
  let e = b();
306
332
  return l({ mutationFn: (t) => e.importDump(t.file, t.database) });
307
333
  }
308
- function I() {
334
+ function L() {
309
335
  let e = b();
310
336
  return l({ mutationFn: (t) => e.importFromStorage(t.storageType, t.storagePath, t.database, t.storageConfig) });
311
337
  }
312
338
  //#endregion
313
339
  //#region src/utils/format.ts
314
- var te = new Intl.DateTimeFormat("pt-BR", {
340
+ var R = new Intl.DateTimeFormat("pt-BR", {
315
341
  day: "2-digit",
316
342
  month: "2-digit",
317
343
  year: "numeric",
318
344
  hour: "2-digit",
319
345
  minute: "2-digit"
320
- }), L = [
346
+ }), z = [
321
347
  "B",
322
348
  "KB",
323
349
  "MB",
324
350
  "GB",
325
351
  "TB"
326
352
  ];
327
- function R(e) {
353
+ function B(e) {
328
354
  if (e == null || e < 0) return "—";
329
355
  if (e < 1024) return `${e} B`;
330
356
  let t = e, n = 0;
331
- for (; t >= 1024 && n < L.length - 1;) t /= 1024, n += 1;
332
- return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${L[n]}`;
357
+ for (; t >= 1024 && n < z.length - 1;) t /= 1024, n += 1;
358
+ return `${Number.isInteger(t) ? t : Number(t.toFixed(1))} ${z[n]}`;
333
359
  }
334
- function z(e) {
360
+ function V(e) {
335
361
  if (!e) return "—";
336
362
  let t = new Date(e);
337
- return Number.isNaN(t.getTime()) ? "—" : te.format(t);
363
+ return Number.isNaN(t.getTime()) ? "—" : R.format(t);
338
364
  }
339
- var B = {
365
+ var H = {
340
366
  PENDING: "Pendente",
341
367
  IN_PROGRESS: "Em andamento",
342
368
  COMPLETED: "Concluído",
@@ -344,7 +370,7 @@ var B = {
344
370
  RESTORING: "Restaurando",
345
371
  RESTORED: "Restaurado",
346
372
  RESTORE_FAILED: "Falha na restauração"
347
- }, V = {
373
+ }, U = {
348
374
  PENDING: "text-bg-secondary",
349
375
  IN_PROGRESS: "text-bg-primary",
350
376
  COMPLETED: "text-bg-success",
@@ -352,25 +378,25 @@ var B = {
352
378
  RESTORING: "text-bg-warning",
353
379
  RESTORED: "text-bg-info",
354
380
  RESTORE_FAILED: "text-bg-danger"
355
- }, H = {
381
+ }, W = {
356
382
  postgresql: "PostgreSQL",
357
383
  postgres: "PostgreSQL",
358
384
  mysql: "MySQL",
359
385
  sqlserver: "SQL Server",
360
386
  mssql: "SQL Server"
361
- }, ne = {
387
+ }, G = {
362
388
  DATABASE: "Banco de dados",
363
389
  STORAGE: "Storage",
364
390
  FULL: "Completo"
365
- }, re = {
391
+ }, K = {
366
392
  DATABASE: "text-bg-primary",
367
393
  STORAGE: "text-bg-info",
368
394
  FULL: "text-bg-secondary"
369
395
  };
370
396
  //#endregion
371
397
  //#region src/components/StatusBadge.tsx
372
- function U({ status: e }) {
373
- let t = V[e] ?? "text-bg-secondary", n = B[e] ?? e;
398
+ function q({ status: e }) {
399
+ let t = U[e] ?? "text-bg-secondary", n = H[e] ?? e;
374
400
  return /* @__PURE__ */ p("span", {
375
401
  className: `badge rounded-pill ${t}`,
376
402
  children: n
@@ -378,7 +404,7 @@ function U({ status: e }) {
378
404
  }
379
405
  //#endregion
380
406
  //#region src/components/DatabaseBanner.tsx
381
- function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
407
+ function J({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
382
408
  if (t) return /* @__PURE__ */ p("div", {
383
409
  className: "mb-3",
384
410
  children: /* @__PURE__ */ m("small", {
@@ -397,7 +423,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
397
423
  "aria-hidden": !0
398
424
  }),
399
425
  "Não foi possível consultar o banco configurado: ",
400
- K(n)
426
+ X(n)
401
427
  ]
402
428
  });
403
429
  if (!e || !e.configured) return i ? /* @__PURE__ */ m("div", {
@@ -419,7 +445,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
419
445
  "). A criação e a importação usam esse banco."
420
446
  ]
421
447
  });
422
- let a = H[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
448
+ let a = W[e.databaseType ?? ""] ?? e.databaseType ?? "postgresql";
423
449
  return i ? /* @__PURE__ */ m("div", {
424
450
  className: "mb-3",
425
451
  children: [
@@ -537,7 +563,7 @@ function W({ dbInfo: e, isLoading: t, error: n, label: r, compact: i = !1 }) {
537
563
  }
538
564
  //#endregion
539
565
  //#region src/components/RestoreModal.tsx
540
- function G({ backup: e, onClose: t }) {
566
+ function Y({ backup: e, onClose: t }) {
541
567
  let n = M(), r = D(), i = () => {
542
568
  n.mutate({ id: e.id });
543
569
  }, a = n.isPending, o = e.backupType === "STORAGE";
@@ -588,7 +614,7 @@ function G({ backup: e, onClose: t }) {
588
614
  " será aplicado no banco configurado abaixo."
589
615
  ] })
590
616
  }),
591
- !o && /* @__PURE__ */ p(W, {
617
+ !o && /* @__PURE__ */ p(J, {
592
618
  label: "Banco de destino",
593
619
  dbInfo: r.data,
594
620
  isLoading: r.isLoading,
@@ -599,7 +625,7 @@ function G({ backup: e, onClose: t }) {
599
625
  children: [/* @__PURE__ */ p("i", {
600
626
  className: "bi bi-x-circle me-2",
601
627
  "aria-hidden": !0
602
- }), K(n.error)]
628
+ }), X(n.error)]
603
629
  }),
604
630
  n.isSuccess && /* @__PURE__ */ m("div", {
605
631
  className: "alert alert-success mt-3 mb-0",
@@ -634,13 +660,13 @@ function G({ backup: e, onClose: t }) {
634
660
  })
635
661
  })] });
636
662
  }
637
- function K(e) {
663
+ function X(e) {
638
664
  return e instanceof Error ? e.message : String(e);
639
665
  }
640
666
  //#endregion
641
667
  //#region src/components/BackupsTab.tsx
642
- function q() {
643
- 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 } = E(e, 10, !0), y = ee(), b = N(), x = P();
668
+ function Z() {
669
+ 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 } = E(e, 10, !0), y = F(), b = N(), x = P();
644
670
  r(() => {
645
671
  if (!y.data || !y.variables) return;
646
672
  let e = URL.createObjectURL(y.data), t = document.createElement("a");
@@ -682,7 +708,7 @@ function q() {
682
708
  "aria-hidden": !0
683
709
  }),
684
710
  "Não foi possível carregar os backups: ",
685
- J(g)
711
+ Q(g)
686
712
  ]
687
713
  }),
688
714
  y.isError && /* @__PURE__ */ m("div", {
@@ -693,7 +719,7 @@ function q() {
693
719
  "aria-hidden": !0
694
720
  }),
695
721
  "Falha no download: ",
696
- J(y.error)
722
+ Q(y.error)
697
723
  ]
698
724
  }),
699
725
  b.isError && /* @__PURE__ */ m("div", {
@@ -704,7 +730,7 @@ function q() {
704
730
  "aria-hidden": !0
705
731
  }),
706
732
  "Falha ao excluir: ",
707
- J(b.error)
733
+ Q(b.error)
708
734
  ]
709
735
  }),
710
736
  x.isError && /* @__PURE__ */ m("div", {
@@ -715,7 +741,7 @@ function q() {
715
741
  "aria-hidden": !0
716
742
  }),
717
743
  "Falha ao verificar: ",
718
- J(x.error)
744
+ Q(x.error)
719
745
  ]
720
746
  }),
721
747
  Object.entries(l).map(([e, t]) => /* @__PURE__ */ m("div", {
@@ -808,18 +834,18 @@ function q() {
808
834
  }), e.filename]
809
835
  }),
810
836
  /* @__PURE__ */ p("td", { children: e.backupType ? /* @__PURE__ */ p("span", {
811
- className: `badge ${re[e.backupType] ?? "text-bg-secondary"}`,
812
- children: ne[e.backupType] ?? e.backupType
837
+ className: `badge ${K[e.backupType] ?? "text-bg-secondary"}`,
838
+ children: G[e.backupType] ?? e.backupType
813
839
  }) : /* @__PURE__ */ p("span", {
814
840
  className: "text-muted",
815
841
  children: "—"
816
842
  }) }),
817
- /* @__PURE__ */ p("td", { children: H[e.databaseType] ?? e.databaseType }),
843
+ /* @__PURE__ */ p("td", { children: W[e.databaseType] ?? e.databaseType }),
818
844
  /* @__PURE__ */ p("td", {
819
845
  className: "text-end",
820
- children: R(e.fileSize)
846
+ children: B(e.fileSize)
821
847
  }),
822
- /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(U, { status: e.status }) }),
848
+ /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p(q, { status: e.status }) }),
823
849
  /* @__PURE__ */ p("td", { children: e.sha256 ? /* @__PURE__ */ m("code", {
824
850
  title: e.sha256,
825
851
  style: { fontSize: "0.8em" },
@@ -842,7 +868,7 @@ function q() {
842
868
  className: "text-muted",
843
869
  children: "—"
844
870
  }) }),
845
- /* @__PURE__ */ p("td", { children: z(e.createdAt) }),
871
+ /* @__PURE__ */ p("td", { children: V(e.createdAt) }),
846
872
  /* @__PURE__ */ m("td", {
847
873
  className: "text-end text-nowrap",
848
874
  children: [
@@ -935,11 +961,11 @@ function q() {
935
961
  })]
936
962
  })]
937
963
  }),
938
- n && /* @__PURE__ */ p(G, {
964
+ n && /* @__PURE__ */ p(Y, {
939
965
  backup: n,
940
966
  onClose: () => a(null)
941
967
  }),
942
- s && /* @__PURE__ */ p(ie, {
968
+ s && /* @__PURE__ */ p(ee, {
943
969
  backup: s,
944
970
  busy: b.isPending,
945
971
  onCancel: () => c(null),
@@ -949,7 +975,7 @@ function q() {
949
975
  })
950
976
  ] });
951
977
  }
952
- function ie(e) {
978
+ function ee(e) {
953
979
  let { backup: t, busy: n, onCancel: r, onConfirm: i } = e;
954
980
  return /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", { className: "modal-backdrop show" }), /* @__PURE__ */ p("div", {
955
981
  className: "modal show d-block",
@@ -1015,12 +1041,12 @@ function ie(e) {
1015
1041
  })
1016
1042
  })] });
1017
1043
  }
1018
- function J(e) {
1044
+ function Q(e) {
1019
1045
  return e instanceof Error ? e.message : String(e);
1020
1046
  }
1021
1047
  //#endregion
1022
1048
  //#region src/components/CreateBackupTab.tsx
1023
- function Y() {
1049
+ function te() {
1024
1050
  let e = j(), t = D(), n = O(t.data?.configured), [r, a] = o(!0), [s, c] = o({
1025
1051
  format: "plain",
1026
1052
  includeData: !0,
@@ -1029,7 +1055,7 @@ function Y() {
1029
1055
  includeIndexes: !0,
1030
1056
  includeConstraints: !0,
1031
1057
  includeRelatedTables: !1
1032
- }), [l, u] = o(() => /* @__PURE__ */ new Set()), [d, f] = o(null), [h, g] = o({}), [_, v] = o(""), y = k(d, t.data?.configured), b = i(() => new Set((t.data?.exportExcludedTables ?? []).map(oe)), [t.data?.exportExcludedTables]), x = i(() => {
1058
+ }), [l, u] = o(() => /* @__PURE__ */ new Set()), [d, f] = o(null), [h, g] = o({}), [_, v] = o(""), y = k(d, t.data?.configured), b = i(() => new Set((t.data?.exportExcludedTables ?? []).map(re)), [t.data?.exportExcludedTables]), x = i(() => {
1033
1059
  let e = n.data ?? [], t = _.trim().toLowerCase();
1034
1060
  return t ? e.filter((e) => e.fullName.toLowerCase().includes(t)) : e;
1035
1061
  }, [n.data, _]), S = i(() => Object.values(h).reduce((e, t) => e + t.length, 0), [h]), C = (e) => {
@@ -1070,7 +1096,7 @@ function Y() {
1070
1096
  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."
1071
1097
  })]
1072
1098
  }),
1073
- /* @__PURE__ */ p(W, {
1099
+ /* @__PURE__ */ p(J, {
1074
1100
  label: "Banco de destino",
1075
1101
  dbInfo: t.data,
1076
1102
  isLoading: t.isLoading,
@@ -1129,7 +1155,7 @@ function Y() {
1129
1155
  "aria-hidden": !0
1130
1156
  }),
1131
1157
  "Falha ao listar tabelas: ",
1132
- K(n.error)
1158
+ X(n.error)
1133
1159
  ]
1134
1160
  }),
1135
1161
  x.length === 0 && !n.isLoading && /* @__PURE__ */ p("div", {
@@ -1145,7 +1171,7 @@ function Y() {
1145
1171
  maxHeight: 420,
1146
1172
  overflow: "auto"
1147
1173
  },
1148
- children: x.map((e) => /* @__PURE__ */ p(ae, {
1174
+ children: x.map((e) => /* @__PURE__ */ p(ne, {
1149
1175
  table: e,
1150
1176
  disabled: b.has(e.fullName),
1151
1177
  selected: l.has(e.fullName),
@@ -1306,7 +1332,7 @@ function Y() {
1306
1332
  "aria-hidden": !0
1307
1333
  }),
1308
1334
  "Falha ao criar backup: ",
1309
- K(e.error)
1335
+ X(e.error)
1310
1336
  ]
1311
1337
  }),
1312
1338
  e.isSuccess && /* @__PURE__ */ m("div", {
@@ -1345,7 +1371,7 @@ function Y() {
1345
1371
  })
1346
1372
  ] });
1347
1373
  }
1348
- function ae({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1374
+ function ne({ table: e, disabled: t, selected: n, expanded: r, excluded: i, detail: a, onToggleTable: o, onToggleExpand: s, onToggleColumn: c }) {
1349
1375
  return /* @__PURE__ */ p("div", {
1350
1376
  className: `card h-100 ${n ? "border-primary" : ""}`,
1351
1377
  style: { minWidth: 0 },
@@ -1409,7 +1435,7 @@ function ae({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1409
1435
  }),
1410
1436
  a?.isError && /* @__PURE__ */ m("small", {
1411
1437
  className: "text-danger",
1412
- children: ["Erro: ", K(a.error)]
1438
+ children: ["Erro: ", X(a.error)]
1413
1439
  }),
1414
1440
  a?.data && /* @__PURE__ */ m(f, { children: [a.data.columns.map((e) => /* @__PURE__ */ m("div", {
1415
1441
  className: "d-flex align-items-center gap-2 py-1",
@@ -1460,13 +1486,13 @@ function ae({ table: e, disabled: t, selected: n, expanded: r, excluded: i, deta
1460
1486
  })
1461
1487
  });
1462
1488
  }
1463
- function oe(e) {
1489
+ function re(e) {
1464
1490
  let t = e.trim();
1465
1491
  return t.includes(".") ? t : `public.${t}`;
1466
1492
  }
1467
1493
  //#endregion
1468
1494
  //#region src/components/DiagnosticsTab.tsx
1469
- function X() {
1495
+ function ie() {
1470
1496
  let { data: e, isLoading: t, isError: n, error: r, refetch: a, isFetching: o } = T(), s = i(() => e ? {
1471
1497
  total: e.length,
1472
1498
  okCount: e.filter((e) => e.ok).length,
@@ -1507,7 +1533,7 @@ function X() {
1507
1533
  "aria-hidden": !0
1508
1534
  }),
1509
1535
  "Não foi possível executar as checagens: ",
1510
- K(r)
1536
+ X(r)
1511
1537
  ]
1512
1538
  }),
1513
1539
  s && /* @__PURE__ */ m("div", {
@@ -1544,7 +1570,7 @@ function X() {
1544
1570
  className: "card",
1545
1571
  children: /* @__PURE__ */ m("ul", {
1546
1572
  className: "list-group list-group-flush",
1547
- children: [(e ?? []).map((e) => /* @__PURE__ */ p(se, { check: e }, e.name)), e && e.length === 0 && /* @__PURE__ */ p("li", {
1573
+ children: [(e ?? []).map((e) => /* @__PURE__ */ p(ae, { check: e }, e.name)), e && e.length === 0 && /* @__PURE__ */ p("li", {
1548
1574
  className: "list-group-item text-center text-muted py-4",
1549
1575
  children: "Nenhuma checagem retornada."
1550
1576
  })]
@@ -1552,7 +1578,7 @@ function X() {
1552
1578
  })
1553
1579
  ] });
1554
1580
  }
1555
- function se({ check: e }) {
1581
+ function ae({ check: e }) {
1556
1582
  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";
1557
1583
  return /* @__PURE__ */ m("li", {
1558
1584
  className: "list-group-item d-flex align-items-start gap-3",
@@ -1588,48 +1614,99 @@ function se({ check: e }) {
1588
1614
  }
1589
1615
  //#endregion
1590
1616
  //#region src/components/StorageExplorerTab.tsx
1591
- function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1592
- 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({
1617
+ var oe = 1500;
1618
+ function $({ target: e = "origin", title: n = "Explorador de Storage", storageType: a = "LOCAL", storageConfig: s }) {
1619
+ let c = b(), h = d(), [g, _] = o(""), [v, y] = o([]), [x, S] = o(/* @__PURE__ */ new Set()), [C, w] = o(null), [T, E] = o(null), [D, O] = o(null), [k, A] = o(!1), j = e === "destination" ? "Destino" : "Origem", { data: M, isPending: N } = u({
1620
+ queryKey: ["storage-config"],
1621
+ queryFn: () => c.getStorageConfig(),
1622
+ retry: !1
1623
+ }), P = M?.[e], F = i(() => P?.configured ? {
1624
+ type: P.type,
1625
+ ...P.localPath ? { localPath: P.localPath } : {},
1626
+ ...P.endpoint ? { endpoint: P.endpoint } : {},
1627
+ ...P.bucketName ? { bucketName: P.bucketName } : {},
1628
+ ...P.region ? { region: P.region } : {},
1629
+ ...P.pathStyleEnabled ? { pathStyleEnabled: !0 } : {}
1630
+ } : s, [P, s]), I = F?.type ?? a, { data: L, isLoading: R } = u({
1593
1631
  queryKey: [
1594
1632
  "storage-files",
1595
- C,
1596
- c,
1597
- n
1633
+ e,
1634
+ I,
1635
+ g,
1636
+ F
1598
1637
  ],
1599
- queryFn: () => i.listStorageFiles(C, c || void 0, n)
1600
- }), E = l({
1601
- mutationFn: (e) => i.deleteStorageFile(e, C, n),
1638
+ queryFn: () => c.listStorageFiles(I, g || void 0, F, e),
1639
+ enabled: !N
1640
+ }), z = l({
1641
+ mutationFn: (t) => c.deleteStorageFile(t, I, F, e),
1602
1642
  onSuccess: () => {
1603
- s.invalidateQueries({ queryKey: ["storage-files"] }), S(null);
1643
+ h.invalidateQueries({ queryKey: ["storage-files"] }), w(null);
1604
1644
  }
1605
- }), D = a(!1), O = t((e) => {
1606
- h(e), _(e ? e.split("/").filter(Boolean) : []), y(/* @__PURE__ */ new Set());
1607
- }, []), k = t((e) => {
1608
- let t = g.slice(0, e + 1).join("/");
1609
- O(t);
1610
- }, [g, O]);
1645
+ }), H = t((e) => {
1646
+ _(e), y(e ? e.split("/").filter(Boolean) : []), S(/* @__PURE__ */ new Set());
1647
+ }, []), U = t((e) => {
1648
+ let t = v.slice(0, e + 1).join("/");
1649
+ H(t);
1650
+ }, [v, H]);
1611
1651
  r(() => {
1612
- _(c ? c.split("/").filter(Boolean) : []);
1613
- }, [c]);
1614
- let A = (e) => {
1615
- y((t) => {
1652
+ y(g ? g.split("/").filter(Boolean) : []);
1653
+ }, [g]);
1654
+ let W = (e) => {
1655
+ S((t) => {
1616
1656
  let n = new Set(t);
1617
1657
  return n.has(e) ? n.delete(e) : n.add(e), n;
1618
1658
  });
1619
- }, j = () => {
1620
- w && (v.size === w.length ? y(/* @__PURE__ */ new Set()) : y(new Set(w.map((e) => e.path))));
1621
- }, M = async (e) => {
1659
+ }, G = () => {
1660
+ L && S((e) => e.size === L.length ? /* @__PURE__ */ new Set() : new Set(L.map((e) => e.path)));
1661
+ }, K = (e, t) => {
1662
+ let n = window.URL.createObjectURL(e), r = document.createElement("a");
1663
+ r.href = n, r.download = t, document.body.appendChild(r), r.click(), r.remove(), window.URL.revokeObjectURL(n);
1664
+ }, q = async (t, n) => {
1665
+ let r = {
1666
+ storage: e,
1667
+ type: I,
1668
+ mode: t,
1669
+ keys: n
1670
+ };
1671
+ F?.localPath && (r.localPath = F.localPath), F?.endpoint && (r.endpoint = F.endpoint), F?.bucketName && (r.bucketName = F.bucketName), F?.accessKey && (r.accessKey = F.accessKey), F?.secretKey && (r.secretKey = F.secretKey), F?.region && (r.region = F.region), F?.pathStyleEnabled && (r.pathStyleEnabled = !0), O(null);
1672
+ let i = await c.createStorageZip(r);
1673
+ E(i);
1674
+ }, J = async (e) => {
1675
+ await q("file", [e]);
1676
+ }, Y = async () => {
1677
+ await q("zip", [...x]);
1678
+ }, X = async (e) => {
1622
1679
  try {
1623
- let t = await i.downloadStorageFile(e, C, n), r = e.split("/").pop() ?? e, a = window.URL.createObjectURL(t), o = document.createElement("a");
1624
- o.href = a, o.download = r, document.body.appendChild(o), o.click(), o.remove(), window.URL.revokeObjectURL(a);
1625
- } catch {}
1626
- }, N = async () => {
1627
- let e = [...v].filter((e) => {
1628
- let t = w?.find((t) => t.path === e);
1629
- return t != null && !t.directory;
1630
- });
1631
- for (let t of e) await M(t);
1632
- }, P = (e) => e ? z(e) : "-";
1680
+ A(!0);
1681
+ let t = await c.downloadStorageZip(e.jobId);
1682
+ K(t, e.fileName ?? `storage_${j.toLowerCase()}.zip`);
1683
+ } catch (e) {
1684
+ O(e instanceof Error ? e.message : String(e));
1685
+ } finally {
1686
+ A(!1), E(null);
1687
+ }
1688
+ };
1689
+ r(() => {
1690
+ let e = T?.jobId;
1691
+ if (!e) return;
1692
+ let t = !1, n;
1693
+ return n = window.setInterval(async () => {
1694
+ let r;
1695
+ try {
1696
+ r = await c.getStorageZipStatus(e);
1697
+ } catch (e) {
1698
+ if (window.clearInterval(n), t) return;
1699
+ O(e instanceof Error ? e.message : String(e)), E(null);
1700
+ return;
1701
+ }
1702
+ t || (E(r), r.status === "DONE" ? (window.clearInterval(n), await X(r)) : r.status === "FAILED" && (window.clearInterval(n), O(r.message)));
1703
+ }, oe), () => {
1704
+ t = !0, window.clearInterval(n);
1705
+ };
1706
+ }, [T?.jobId]);
1707
+ let Z = () => {
1708
+ E(null), O(null);
1709
+ }, ee = (e) => e ? V(e) : "-";
1633
1710
  return /* @__PURE__ */ m("div", {
1634
1711
  className: "space-y-3",
1635
1712
  children: [
@@ -1639,53 +1716,71 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1639
1716
  className: "mb-0",
1640
1717
  children: [
1641
1718
  /* @__PURE__ */ p("i", { className: "bi bi-folder2-open me-2" }),
1642
- "Explorador de Storage",
1719
+ n,
1643
1720
  /* @__PURE__ */ p("span", {
1644
1721
  className: "badge bg-secondary ms-2",
1645
- children: C
1722
+ children: j
1723
+ }),
1724
+ /* @__PURE__ */ p("span", {
1725
+ className: "badge bg-light text-dark border ms-1",
1726
+ children: I
1646
1727
  })
1647
1728
  ]
1648
1729
  }), /* @__PURE__ */ p("div", {
1649
1730
  className: "d-flex gap-2",
1650
- children: v.size > 0 && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ m("button", {
1731
+ children: x.size > 0 && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ m("button", {
1651
1732
  className: "btn btn-sm btn-outline-primary",
1652
- onClick: N,
1733
+ onClick: Y,
1653
1734
  children: [
1654
1735
  /* @__PURE__ */ p("i", { className: "bi bi-download me-1" }),
1655
- "Baixar (",
1656
- v.size,
1736
+ "Baixar seleção (",
1737
+ x.size,
1657
1738
  ")"
1658
1739
  ]
1659
1740
  }), /* @__PURE__ */ m("button", {
1660
1741
  className: "btn btn-sm btn-outline-danger",
1661
- onClick: () => S("multiple"),
1742
+ onClick: () => w("multiple"),
1662
1743
  children: [
1663
1744
  /* @__PURE__ */ p("i", { className: "bi bi-trash me-1" }),
1664
1745
  "Excluir (",
1665
- v.size,
1746
+ x.size,
1666
1747
  ")"
1667
1748
  ]
1668
1749
  })] })
1669
1750
  })]
1670
1751
  }),
1752
+ !N && !P?.configured && !s && /* @__PURE__ */ m("div", {
1753
+ className: "alert alert-warning small py-1 mb-2",
1754
+ children: [
1755
+ /* @__PURE__ */ p("i", {
1756
+ className: "bi bi-exclamation-triangle me-1",
1757
+ "aria-hidden": !0
1758
+ }),
1759
+ "Servidor não configurou este storage (",
1760
+ e === "destination" ? "OUTPUT_STORAGE_*" : "ORIGIN_STORAGE_*",
1761
+ "). Configure as variáveis de ambiente ou envie ",
1762
+ /* @__PURE__ */ p("code", { children: "storageConfig" }),
1763
+ " via props."
1764
+ ]
1765
+ }),
1671
1766
  /* @__PURE__ */ p("nav", {
1672
1767
  "aria-label": "breadcrumb",
1673
1768
  children: /* @__PURE__ */ m("ol", {
1674
1769
  className: "breadcrumb mb-0 small",
1675
1770
  children: [/* @__PURE__ */ p("li", {
1676
- className: `breadcrumb-item ${g.length === 0 ? "active" : ""}`,
1677
- style: { cursor: g.length > 0 ? "pointer" : "default" },
1678
- onClick: () => O(""),
1771
+ className: `breadcrumb-item ${v.length === 0 ? "active" : ""}`,
1772
+ style: { cursor: v.length > 0 ? "pointer" : "default" },
1773
+ onClick: () => H(""),
1679
1774
  children: /* @__PURE__ */ p("i", { className: "bi bi-house" })
1680
- }), g.map((e, t) => /* @__PURE__ */ p("li", {
1681
- className: `breadcrumb-item ${t === g.length - 1 ? "active" : ""}`,
1682
- style: { cursor: t < g.length - 1 ? "pointer" : "default" },
1683
- onClick: () => t < g.length - 1 && k(t),
1775
+ }), v.map((e, t) => /* @__PURE__ */ p("li", {
1776
+ className: `breadcrumb-item ${t === v.length - 1 ? "active" : ""}`,
1777
+ style: { cursor: t < v.length - 1 ? "pointer" : "default" },
1778
+ onClick: () => t < v.length - 1 && U(t),
1684
1779
  children: e
1685
1780
  }, t))]
1686
1781
  })
1687
1782
  }),
1688
- T ? /* @__PURE__ */ m("div", {
1783
+ R ? /* @__PURE__ */ m("div", {
1689
1784
  className: "text-center py-5",
1690
1785
  children: [/* @__PURE__ */ p("div", {
1691
1786
  className: "spinner-border text-primary",
@@ -1694,14 +1789,14 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1694
1789
  className: "mt-2 small text-muted",
1695
1790
  children: "Carregando arquivos..."
1696
1791
  })]
1697
- }) : !w || w.length === 0 ? /* @__PURE__ */ m("div", {
1792
+ }) : !L || L.length === 0 ? /* @__PURE__ */ m("div", {
1698
1793
  className: "text-center py-5 text-muted",
1699
1794
  children: [
1700
1795
  /* @__PURE__ */ p("i", { className: "bi bi-folder-x fs-1 d-block mb-2" }),
1701
1796
  "Nenhum arquivo encontrado",
1702
- c && /* @__PURE__ */ p("button", {
1797
+ g && /* @__PURE__ */ p("button", {
1703
1798
  className: "btn btn-sm btn-link mt-2",
1704
- onClick: () => O(""),
1799
+ onClick: () => H(""),
1705
1800
  children: "Voltar ao inicio"
1706
1801
  })
1707
1802
  ]
@@ -1709,11 +1804,11 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1709
1804
  className: "d-flex align-items-center gap-2 mb-2",
1710
1805
  children: [/* @__PURE__ */ m("button", {
1711
1806
  className: "btn btn-sm btn-outline-secondary",
1712
- onClick: j,
1713
- children: [v.size === w.length ? "Desmarcar" : "Selecionar", " Todos"]
1807
+ onClick: G,
1808
+ children: [x.size === L.length ? "Desmarcar" : "Selecionar", " Todos"]
1714
1809
  }), /* @__PURE__ */ m("span", {
1715
1810
  className: "small text-muted",
1716
- children: [w.length, " item(ns)"]
1811
+ children: [L.length, " item(ns)"]
1717
1812
  })]
1718
1813
  }), /* @__PURE__ */ p("div", {
1719
1814
  className: "table-responsive",
@@ -1737,18 +1832,18 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1737
1832
  children: "Acoes"
1738
1833
  })
1739
1834
  ] })
1740
- }), /* @__PURE__ */ p("tbody", { children: w.map((e) => /* @__PURE__ */ m("tr", {
1741
- className: v.has(e.path) ? "table-active" : "",
1835
+ }), /* @__PURE__ */ p("tbody", { children: L.map((e) => /* @__PURE__ */ m("tr", {
1836
+ className: x.has(e.path) ? "table-active" : "",
1742
1837
  children: [
1743
1838
  /* @__PURE__ */ p("td", { children: /* @__PURE__ */ p("input", {
1744
1839
  type: "checkbox",
1745
1840
  className: "form-check-input",
1746
- checked: v.has(e.path),
1747
- onChange: () => A(e.path)
1841
+ checked: x.has(e.path),
1842
+ onChange: () => W(e.path)
1748
1843
  }) }),
1749
1844
  /* @__PURE__ */ p("td", { children: e.directory ? /* @__PURE__ */ m("button", {
1750
1845
  className: "btn btn-sm btn-link text-decoration-none p-0 text-start",
1751
- onClick: () => O(e.path),
1846
+ onClick: () => H(e.path),
1752
1847
  children: [/* @__PURE__ */ p("i", { className: "bi bi-folder-fill text-warning me-2" }), /* @__PURE__ */ p("span", {
1753
1848
  className: "font-monospace",
1754
1849
  children: e.name
@@ -1759,23 +1854,23 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1759
1854
  }) }),
1760
1855
  /* @__PURE__ */ p("td", {
1761
1856
  className: "text-muted small",
1762
- children: e.directory ? "-" : R(e.size)
1857
+ children: e.directory ? "-" : B(e.size)
1763
1858
  }),
1764
1859
  /* @__PURE__ */ p("td", {
1765
1860
  className: "text-muted small",
1766
- children: P(e.lastModified)
1861
+ children: ee(e.lastModified)
1767
1862
  }),
1768
1863
  /* @__PURE__ */ p("td", { children: !e.directory && /* @__PURE__ */ m("div", {
1769
1864
  className: "d-flex gap-1",
1770
1865
  children: [/* @__PURE__ */ p("button", {
1771
1866
  className: "btn btn-sm btn-outline-primary py-0",
1772
1867
  title: "Baixar",
1773
- onClick: () => M(e.path),
1868
+ onClick: () => J(e.path),
1774
1869
  children: /* @__PURE__ */ p("i", { className: "bi bi-download" })
1775
1870
  }), /* @__PURE__ */ p("button", {
1776
1871
  className: "btn btn-sm btn-outline-danger py-0",
1777
1872
  title: "Excluir",
1778
- onClick: () => S(e.path),
1873
+ onClick: () => w(e.path),
1779
1874
  children: /* @__PURE__ */ p("i", { className: "bi bi-trash" })
1780
1875
  })]
1781
1876
  }) })
@@ -1783,7 +1878,139 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1783
1878
  }, e.path)) })]
1784
1879
  })
1785
1880
  })] }),
1786
- x && /* @__PURE__ */ p("div", {
1881
+ T && /* @__PURE__ */ p("div", {
1882
+ className: "modal d-block",
1883
+ tabIndex: -1,
1884
+ style: { background: "rgba(0,0,0,.5)" },
1885
+ children: /* @__PURE__ */ p("div", {
1886
+ className: "modal-dialog modal-dialog-centered",
1887
+ children: /* @__PURE__ */ m("div", {
1888
+ className: "modal-content",
1889
+ children: [
1890
+ /* @__PURE__ */ m("div", {
1891
+ className: "modal-header",
1892
+ children: [/* @__PURE__ */ m("h6", {
1893
+ className: "modal-title",
1894
+ children: [/* @__PURE__ */ p("i", { className: "bi bi-box-arrow-down me-2" }), T.mode === "ZIP" ? "Compactando e baixando" : "Baixando arquivo"]
1895
+ }), /* @__PURE__ */ p("button", {
1896
+ className: "btn-close",
1897
+ onClick: Z
1898
+ })]
1899
+ }),
1900
+ /* @__PURE__ */ m("div", {
1901
+ className: "modal-body",
1902
+ children: [
1903
+ /* @__PURE__ */ m("div", {
1904
+ className: "d-flex align-items-center gap-3 mb-3",
1905
+ children: [/* @__PURE__ */ p("div", {
1906
+ className: "spinner-border text-primary flex-shrink-0",
1907
+ style: {
1908
+ width: "1.5rem",
1909
+ height: "1.5rem"
1910
+ },
1911
+ role: "status"
1912
+ }), /* @__PURE__ */ m("div", {
1913
+ className: "w-100",
1914
+ children: [/* @__PURE__ */ m("div", {
1915
+ className: "d-flex justify-content-between small mb-1",
1916
+ children: [/* @__PURE__ */ p("span", { children: ((e) => e.mode === "FILE" ? e.phase === "DOWNLOADING" ? e.totalBytes > 0 ? `Baixando arquivo... ${B(e.processedBytes)} / ${B(e.totalBytes)}` : "Baixando arquivo..." : e.message || "Preparando download..." : e.phase === "PREPARING" ? `Preparando ${e.processedItems}/${e.totalItems} arquivo(s)...` : e.phase === "COMPRESSING" ? `Compactando... ${e.percent}%` : e.message || "Processando...")(T) }), /* @__PURE__ */ m("span", {
1917
+ className: "text-muted",
1918
+ children: [T.percent, "%"]
1919
+ })]
1920
+ }), /* @__PURE__ */ p("div", {
1921
+ className: "progress",
1922
+ style: { height: 8 },
1923
+ children: /* @__PURE__ */ p("div", {
1924
+ className: "progress-bar progress-bar-striped progress-bar-animated",
1925
+ role: "progressbar",
1926
+ style: { width: `${Math.max(T.percent, 3)}%` }
1927
+ })
1928
+ })]
1929
+ })]
1930
+ }),
1931
+ T.currentItem && /* @__PURE__ */ m("div", {
1932
+ className: "small text-muted",
1933
+ title: T.currentItem,
1934
+ children: [/* @__PURE__ */ p("i", {
1935
+ className: "bi bi-file-earmark-text me-1",
1936
+ "aria-hidden": !0
1937
+ }), /* @__PURE__ */ p("span", {
1938
+ className: "font-monospace",
1939
+ children: T.currentItem
1940
+ })]
1941
+ }),
1942
+ T.mode === "FILE" && T.totalBytes > 0 || T.mode === "ZIP" ? /* @__PURE__ */ m("div", {
1943
+ className: "small text-muted mt-1",
1944
+ children: [
1945
+ /* @__PURE__ */ p("i", {
1946
+ className: "bi bi-hdd me-1",
1947
+ "aria-hidden": !0
1948
+ }),
1949
+ B(T.processedBytes),
1950
+ T.totalBytes > 0 ? ` / ${B(T.totalBytes)}` : ""
1951
+ ]
1952
+ }) : null,
1953
+ T.status === "FAILED" && /* @__PURE__ */ m("div", {
1954
+ className: "alert alert-danger small mt-2 mb-0 py-1",
1955
+ children: [/* @__PURE__ */ p("i", {
1956
+ className: "bi bi-exclamation-triangle me-1",
1957
+ "aria-hidden": !0
1958
+ }), T.message || "Falha ao preparar o download."]
1959
+ })
1960
+ ]
1961
+ }),
1962
+ /* @__PURE__ */ p("div", {
1963
+ className: "modal-footer",
1964
+ children: /* @__PURE__ */ p("button", {
1965
+ className: "btn btn-sm btn-secondary",
1966
+ onClick: Z,
1967
+ disabled: k,
1968
+ children: k ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("span", { className: "spinner-border spinner-border-sm me-1" }), "Baixando..."] }) : T.status === "DONE" || T.status === "FAILED" ? "Fechar" : "Fechar (continua em 2º plano)"
1969
+ })
1970
+ })
1971
+ ]
1972
+ })
1973
+ })
1974
+ }),
1975
+ D && !T && /* @__PURE__ */ p("div", {
1976
+ className: "modal d-block",
1977
+ tabIndex: -1,
1978
+ style: { background: "rgba(0,0,0,.5)" },
1979
+ children: /* @__PURE__ */ p("div", {
1980
+ className: "modal-dialog modal-dialog-centered",
1981
+ children: /* @__PURE__ */ m("div", {
1982
+ className: "modal-content",
1983
+ children: [
1984
+ /* @__PURE__ */ m("div", {
1985
+ className: "modal-header",
1986
+ children: [/* @__PURE__ */ m("h6", {
1987
+ className: "modal-title",
1988
+ children: [/* @__PURE__ */ p("i", { className: "bi bi-exclamation-triangle me-2 text-danger" }), "Falha no download"]
1989
+ }), /* @__PURE__ */ p("button", {
1990
+ className: "btn-close",
1991
+ onClick: () => O(null)
1992
+ })]
1993
+ }),
1994
+ /* @__PURE__ */ p("div", {
1995
+ className: "modal-body",
1996
+ children: /* @__PURE__ */ p("p", {
1997
+ className: "mb-0 text-danger small",
1998
+ children: D
1999
+ })
2000
+ }),
2001
+ /* @__PURE__ */ p("div", {
2002
+ className: "modal-footer",
2003
+ children: /* @__PURE__ */ p("button", {
2004
+ className: "btn btn-sm btn-secondary",
2005
+ onClick: () => O(null),
2006
+ children: "Fechar"
2007
+ })
2008
+ })
2009
+ ]
2010
+ })
2011
+ })
2012
+ }),
2013
+ C && /* @__PURE__ */ p("div", {
1787
2014
  className: "modal d-block",
1788
2015
  tabIndex: -1,
1789
2016
  style: { background: "rgba(0,0,0,.5)" },
@@ -1799,26 +2026,26 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1799
2026
  children: "Confirmar exclusao"
1800
2027
  }), /* @__PURE__ */ p("button", {
1801
2028
  className: "btn-close",
1802
- onClick: () => S(null)
2029
+ onClick: () => w(null)
1803
2030
  })]
1804
2031
  }),
1805
2032
  /* @__PURE__ */ m("div", {
1806
2033
  className: "modal-body",
1807
2034
  children: [
1808
- x === "multiple" ? /* @__PURE__ */ m("p", { children: [
2035
+ C === "multiple" ? /* @__PURE__ */ m("p", { children: [
1809
2036
  "Excluir ",
1810
- v.size,
2037
+ x.size,
1811
2038
  " arquivo(s) selecionado(s)?"
1812
2039
  ] }) : /* @__PURE__ */ m("p", { children: [
1813
2040
  "Excluir ",
1814
- /* @__PURE__ */ p("code", { children: x.split("/").pop() }),
2041
+ /* @__PURE__ */ p("code", { children: C.split("/").pop() }),
1815
2042
  "?"
1816
2043
  ] }),
1817
2044
  /* @__PURE__ */ p("p", {
1818
2045
  className: "text-danger small mb-0",
1819
2046
  children: "Esta acao nao pode ser desfeita."
1820
2047
  }),
1821
- E.isError && /* @__PURE__ */ m("div", {
2048
+ z.isError && /* @__PURE__ */ m("div", {
1822
2049
  className: "alert alert-danger small mt-2 mb-0 py-1",
1823
2050
  children: [
1824
2051
  /* @__PURE__ */ p("i", {
@@ -1827,7 +2054,7 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1827
2054
  }),
1828
2055
  "Falha ao excluir:",
1829
2056
  " ",
1830
- E.error instanceof Error ? E.error.message : String(E.error)
2057
+ z.error instanceof Error ? z.error.message : String(z.error)
1831
2058
  ]
1832
2059
  })
1833
2060
  ]
@@ -1836,23 +2063,16 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1836
2063
  className: "modal-footer",
1837
2064
  children: [/* @__PURE__ */ p("button", {
1838
2065
  className: "btn btn-sm btn-secondary",
1839
- onClick: () => S(null),
2066
+ onClick: () => w(null),
1840
2067
  children: "Cancelar"
1841
2068
  }), /* @__PURE__ */ p("button", {
1842
2069
  className: "btn btn-sm btn-danger",
1843
- disabled: E.isPending,
2070
+ disabled: z.isPending,
1844
2071
  onClick: async () => {
1845
- if (!D.current) {
1846
- D.current = !0;
1847
- try {
1848
- if (x === "multiple") for (let e of v) await E.mutateAsync(e);
1849
- else await E.mutateAsync(x);
1850
- } finally {
1851
- D.current = !1;
1852
- }
1853
- }
2072
+ if (C === "multiple") for (let e of x) await z.mutateAsync(e);
2073
+ else await z.mutateAsync(C);
1854
2074
  },
1855
- children: E.isPending ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("span", { className: "spinner-border spinner-border-sm me-1" }), "Excluindo..."] }) : "Excluir"
2075
+ children: z.isPending ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("span", { className: "spinner-border spinner-border-sm me-1" }), "Excluindo..."] }) : "Excluir"
1856
2076
  })]
1857
2077
  })
1858
2078
  ]
@@ -1864,8 +2084,8 @@ function Z({ storageType: e = "LOCAL", storageConfig: n }) {
1864
2084
  }
1865
2085
  //#endregion
1866
2086
  //#region src/components/ImportDumpTab.tsx
1867
- function Q() {
1868
- let e = F(), t = I(), n = D(), r = a(null), [i, s] = o("upload"), [c, l] = o(null), [u, d] = o(""), [f, h] = o(!1), g = () => {
2087
+ function se() {
2088
+ let e = I(), t = L(), n = D(), r = a(null), [i, s] = o("upload"), [c, l] = o(null), [u, d] = o(""), [f, h] = o(!1), g = () => {
1869
2089
  i === "upload" && c ? e.mutate({ file: c }) : i === "storage" && u && t.mutate({
1870
2090
  storageType: "LOCAL",
1871
2091
  storagePath: u
@@ -1987,7 +2207,7 @@ function Q() {
1987
2207
  maxHeight: 350,
1988
2208
  overflow: "auto"
1989
2209
  },
1990
- children: /* @__PURE__ */ p(Z, {
2210
+ children: /* @__PURE__ */ p($, {
1991
2211
  storageType: "LOCAL",
1992
2212
  storageConfig: {
1993
2213
  type: "LOCAL",
@@ -2010,7 +2230,7 @@ function Q() {
2010
2230
  children: [/* @__PURE__ */ p("p", {
2011
2231
  className: "small text-body-secondary mb-3",
2012
2232
  children: "O dump será aplicado no banco configurado no servidor. Tabelas bloqueadas por segurança são ignoradas automaticamente na importação."
2013
- }), /* @__PURE__ */ p(W, {
2233
+ }), /* @__PURE__ */ p(J, {
2014
2234
  label: "Banco de destino",
2015
2235
  dbInfo: n.data,
2016
2236
  isLoading: n.isLoading,
@@ -2026,7 +2246,7 @@ function Q() {
2026
2246
  "aria-hidden": !0
2027
2247
  }),
2028
2248
  "Falha na importacao: ",
2029
- K(y)
2249
+ X(y)
2030
2250
  ]
2031
2251
  }),
2032
2252
  v && /* @__PURE__ */ m("div", {
@@ -2134,7 +2354,7 @@ function ce() {
2134
2354
  "aria-hidden": !0
2135
2355
  }),
2136
2356
  "Falha ao criar backup de storage: ",
2137
- K(e.error)
2357
+ X(e.error)
2138
2358
  ]
2139
2359
  }),
2140
2360
  e.isSuccess && /* @__PURE__ */ m("div", {
@@ -2196,13 +2416,13 @@ function le({ label: e, configured: t, ok: n, message: r }) {
2196
2416
  }
2197
2417
  //#endregion
2198
2418
  //#region src/hooks/useAutoTheme.ts
2199
- function $() {
2419
+ function ue() {
2200
2420
  return typeof document < "u" && document.documentElement.classList.contains("dark");
2201
2421
  }
2202
- function ue(e) {
2203
- let [t, n] = o($), [i, a] = o(() => typeof window < "u" && window.matchMedia?.("(prefers-color-scheme: dark)")?.matches === !0);
2422
+ function de(e) {
2423
+ let [t, n] = o(ue), [i, a] = o(() => typeof window < "u" && window.matchMedia?.("(prefers-color-scheme: dark)")?.matches === !0);
2204
2424
  return r(() => {
2205
- let e = document.documentElement, t = new MutationObserver(() => n($()));
2425
+ let e = document.documentElement, t = new MutationObserver(() => n(ue()));
2206
2426
  return t.observe(e, {
2207
2427
  attributes: !0,
2208
2428
  attributeFilter: ["class"]
@@ -2215,8 +2435,8 @@ function ue(e) {
2215
2435
  }
2216
2436
  //#endregion
2217
2437
  //#region src/components/MinivaultModule.tsx
2218
- function de(e) {
2219
- let { companyAcronym: t = "Minivault", theme: n, refetchIntervalMs: r = 15e3, storageType: i = "LOCAL", ...a } = e, l = ue(n), [u] = o(() => new s({ defaultOptions: { queries: {
2438
+ function fe(e) {
2439
+ let { companyAcronym: t = "Minivault", theme: n, refetchIntervalMs: r = 15e3, storageType: i = "LOCAL", ...a } = e, l = de(n), [u] = o(() => new s({ defaultOptions: { queries: {
2220
2440
  refetchInterval: r,
2221
2441
  retry: 1
2222
2442
  } } }));
@@ -2227,15 +2447,15 @@ function de(e) {
2227
2447
  children: /* @__PURE__ */ m("div", {
2228
2448
  className: "minivault-module h-full w-full overflow-auto",
2229
2449
  "data-minivault-theme": l,
2230
- children: [/* @__PURE__ */ p(fe, {
2450
+ children: [/* @__PURE__ */ p(pe, {
2231
2451
  companyAcronym: t,
2232
2452
  theme: l
2233
- }), /* @__PURE__ */ p(pe, { storageType: i })]
2453
+ }), /* @__PURE__ */ p(me, { storageType: i })]
2234
2454
  })
2235
2455
  })
2236
2456
  });
2237
2457
  }
2238
- function fe({ companyAcronym: e, theme: t }) {
2458
+ function pe({ companyAcronym: e, theme: t }) {
2239
2459
  return /* @__PURE__ */ m("div", {
2240
2460
  className: "d-flex align-items-center justify-content-between flex-wrap gap-2 px-3 py-2 border-bottom",
2241
2461
  style: {
@@ -2264,7 +2484,7 @@ function fe({ companyAcronym: e, theme: t }) {
2264
2484
  })]
2265
2485
  });
2266
2486
  }
2267
- function pe({ storageType: e }) {
2487
+ function me({ storageType: e }) {
2268
2488
  let [t, n] = o("backups");
2269
2489
  return /* @__PURE__ */ m("div", {
2270
2490
  className: "p-3",
@@ -2346,23 +2566,40 @@ function pe({ storageType: e }) {
2346
2566
  })
2347
2567
  ]
2348
2568
  }),
2349
- t === "backups" && /* @__PURE__ */ p(q, {}),
2350
- t === "create" && /* @__PURE__ */ p(Y, {}),
2569
+ t === "backups" && /* @__PURE__ */ p(Z, {}),
2570
+ t === "create" && /* @__PURE__ */ p(te, {}),
2351
2571
  t === "createStorage" && /* @__PURE__ */ p(ce, {}),
2352
- t === "import" && /* @__PURE__ */ p(Q, {}),
2353
- t === "storage" && /* @__PURE__ */ p(Z, { storageType: e }),
2354
- t === "diagnostics" && /* @__PURE__ */ p(X, {})
2572
+ t === "import" && /* @__PURE__ */ p(se, {}),
2573
+ t === "storage" && /* @__PURE__ */ m("div", {
2574
+ className: "row g-3",
2575
+ children: [/* @__PURE__ */ p("div", {
2576
+ className: "col-lg-6",
2577
+ children: /* @__PURE__ */ p($, {
2578
+ target: "origin",
2579
+ storageType: e,
2580
+ title: "Storage de Origem"
2581
+ })
2582
+ }), /* @__PURE__ */ p("div", {
2583
+ className: "col-lg-6",
2584
+ children: /* @__PURE__ */ p($, {
2585
+ target: "destination",
2586
+ storageType: e,
2587
+ title: "Storage de Destino"
2588
+ })
2589
+ })]
2590
+ }),
2591
+ t === "diagnostics" && /* @__PURE__ */ p(ie, {})
2355
2592
  ]
2356
2593
  });
2357
2594
  }
2358
2595
  //#endregion
2359
2596
  //#region src/components/CredentialsFields.tsx
2360
- var me = [
2597
+ var he = [
2361
2598
  "postgresql",
2362
2599
  "mysql",
2363
2600
  "sqlserver"
2364
2601
  ];
2365
- function he() {
2602
+ function ge() {
2366
2603
  return {
2367
2604
  host: "localhost",
2368
2605
  port: 5432,
@@ -2372,19 +2609,19 @@ function he() {
2372
2609
  databaseType: "postgresql"
2373
2610
  };
2374
2611
  }
2375
- function ge(e) {
2612
+ function _e(e) {
2376
2613
  return e === "postgresql" || e === "postgres" ? 5432 : e === "mysql" ? 3306 : 1433;
2377
2614
  }
2378
- function _e({ value: e, onChange: t, onValidatedChange: n }) {
2615
+ function ve({ value: e, onChange: t, onValidatedChange: n }) {
2379
2616
  let r = {
2380
- ...he(),
2617
+ ...ge(),
2381
2618
  ...e
2382
2619
  }, i = (e) => {
2383
2620
  let n = {
2384
2621
  ...r,
2385
2622
  databaseType: e
2386
2623
  };
2387
- (r.port === ge(r.databaseType) || !r.port) && (n.port = ge(e)), t(n);
2624
+ (r.port === _e(r.databaseType) || !r.port) && (n.port = _e(e)), t(n);
2388
2625
  }, a = (e) => {
2389
2626
  let i = {
2390
2627
  ...r,
@@ -2406,9 +2643,9 @@ function _e({ value: e, onChange: t, onValidatedChange: n }) {
2406
2643
  className: "form-select",
2407
2644
  value: r.databaseType,
2408
2645
  onChange: (e) => i(e.target.value),
2409
- children: me.map((e) => /* @__PURE__ */ p("option", {
2646
+ children: he.map((e) => /* @__PURE__ */ p("option", {
2410
2647
  value: e,
2411
- children: H[e] ?? e
2648
+ children: W[e] ?? e
2412
2649
  }, e))
2413
2650
  })]
2414
2651
  }),
@@ -2480,4 +2717,4 @@ function _e({ value: e, onChange: t, onValidatedChange: n }) {
2480
2717
  });
2481
2718
  }
2482
2719
  //#endregion
2483
- export { q as BackupsTab, Y as CreateBackupTab, _e 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, de as MinivaultModule, G as RestoreModal, V as STATUS_BADGE, B as STATUS_LABELS, U as StatusBadge, ce as StorageBackupTab, Z as StorageExplorerTab, z as formatDate, R as formatFileSize, E as useBackups, j as useCreateBackup, D as useDbInfo, A as useDbRelationships, k as useDbTableDetail, O as useDbTables, N as useDeleteBackup, T as useDiagnostics, ee as useDownloadBackup, F as useImportDump, I as useImportFromStorage, b as useMinivaultApi, M as useRestoreBackup };
2720
+ export { Z as BackupsTab, te as CreateBackupTab, ve as CredentialsFields, W as DATABASE_TYPE_LABELS, J as DatabaseBanner, ie as DiagnosticsTab, se as ImportDumpTab, g as MinivaultApi, h as MinivaultApiError, y as MinivaultApiProvider, fe as MinivaultModule, Y as RestoreModal, U as STATUS_BADGE, H as STATUS_LABELS, q as StatusBadge, ce as StorageBackupTab, $ as StorageExplorerTab, V as formatDate, B as formatFileSize, E as useBackups, j as useCreateBackup, D as useDbInfo, A as useDbRelationships, k as useDbTableDetail, O as useDbTables, N as useDeleteBackup, T as useDiagnostics, F as useDownloadBackup, I as useImportDump, L as useImportFromStorage, b as useMinivaultApi, M as useRestoreBackup };
@@ -138,6 +138,61 @@ export interface StorageFileItem {
138
138
  lastModified: string | null;
139
139
  directory: boolean;
140
140
  }
141
+ /** Qual storage o explorador manipula: origem (ORIGIN_STORAGE_*) ou destino (OUTPUT_STORAGE_*). */
142
+ export type StorageTarget = 'origin' | 'destination';
143
+ /**
144
+ * Descrição pública da configuração resolvida de um storage no servidor
145
+ * (via GET /api/storage/config). Nunca contém accessKey/secretKey.
146
+ */
147
+ export interface ResolvedStorageInfo {
148
+ storage: StorageTarget;
149
+ type: string;
150
+ localPath: string | null;
151
+ endpoint: string | null;
152
+ bucketName: string | null;
153
+ region: string | null;
154
+ pathStyleEnabled: boolean;
155
+ /** true se houver ao menos uma variável de ambiente do prefixo configurada no servidor. */
156
+ configured: boolean;
157
+ }
158
+ export interface AllStorageConfig {
159
+ origin: ResolvedStorageInfo;
160
+ destination: ResolvedStorageInfo;
161
+ }
162
+ export type ZipJobStatusName = 'PENDING' | 'RUNNING' | 'DONE' | 'FAILED';
163
+ export type ZipJobMode = 'ZIP' | 'FILE';
164
+ export type ZipJobPhase = 'PREPARING' | 'COMPRESSING' | 'DOWNLOADING';
165
+ export interface ZipJobStatus {
166
+ jobId: string;
167
+ status: ZipJobStatusName;
168
+ mode: ZipJobMode;
169
+ phase: ZipJobPhase | null;
170
+ message: string;
171
+ fileName: string | null;
172
+ fileSize: number | null;
173
+ totalItems: number;
174
+ processedItems: number;
175
+ processedBytes: number;
176
+ totalBytes: number;
177
+ percent: number;
178
+ currentItem: string | null;
179
+ createdAt: string;
180
+ completedAt: string | null;
181
+ }
182
+ export type StorageDownloadMode = 'zip' | 'file';
183
+ export interface StorageZipRequest {
184
+ storage: StorageTarget;
185
+ type?: string;
186
+ mode?: StorageDownloadMode;
187
+ keys: string[];
188
+ localPath?: string;
189
+ endpoint?: string;
190
+ bucketName?: string;
191
+ region?: string;
192
+ accessKey?: string;
193
+ secretKey?: string;
194
+ pathStyleEnabled?: boolean;
195
+ }
141
196
  export interface ApiErrorBody {
142
197
  timestamp?: string;
143
198
  status?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@v1ct0rbr/minivault-web",
3
- "version": "0.6.4",
3
+ "version": "0.7.0",
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",