Restrict tenant imports to new tenants
This commit is contained in:
@@ -11,30 +11,15 @@ const tenantId = Number(route.params.id)
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const exportingTenant = ref(false)
|
||||
const importingTenant = ref(false)
|
||||
const tenantExportProgress = ref<null | {
|
||||
status: string
|
||||
filesDone: number
|
||||
filesTotal: number
|
||||
filename: string
|
||||
}>(null)
|
||||
const tenantImportProgress = ref<null | {
|
||||
status: string
|
||||
filesDone: number
|
||||
filesTotal: number
|
||||
filename: string
|
||||
}>(null)
|
||||
const creatingUser = ref(false)
|
||||
const createUserModalOpen = ref(false)
|
||||
const createdUserPassword = ref("")
|
||||
const importFileInput = ref<HTMLInputElement | null>(null)
|
||||
const lastImportResult = ref<null | {
|
||||
tenantId: number
|
||||
tableCount: number
|
||||
rowCount: number
|
||||
restoredFiles: number
|
||||
skippedFiles: number
|
||||
}>(null)
|
||||
const lockedOptions = [
|
||||
{ label: "Aktiv", value: null },
|
||||
{ label: "Tenant-Wartung", value: "maintenance_tenant" },
|
||||
@@ -165,111 +150,6 @@ const downloadTenantExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const openImportFileDialog = () => {
|
||||
importFileInput.value?.click()
|
||||
}
|
||||
|
||||
const importTenantExport = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement
|
||||
const file = input.files?.[0]
|
||||
if (!file || importingTenant.value) return
|
||||
|
||||
importingTenant.value = true
|
||||
tenantImportProgress.value = null
|
||||
lastImportResult.value = null
|
||||
|
||||
try {
|
||||
const targetTenantId = tenantForm.value?.id || tenantId
|
||||
const isZipExport = file.name.endsWith(".zip") || file.name.endsWith(".fedeo-export.zip") || file.type === "application/zip"
|
||||
let result
|
||||
|
||||
if (isZipExport) {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
formData.append("targetTenantId", String(targetTenantId))
|
||||
result = await admin.importTenant(formData)
|
||||
} else {
|
||||
result = await admin.importTenant({
|
||||
exportData: JSON.parse(await file.text()),
|
||||
targetTenantId,
|
||||
})
|
||||
}
|
||||
|
||||
const importJobId = result.importId || result.exportId
|
||||
if (importJobId) {
|
||||
let job = result
|
||||
|
||||
tenantImportProgress.value = {
|
||||
status: job.status,
|
||||
filesDone: job.filesDone || 0,
|
||||
filesTotal: job.filesTotal || 0,
|
||||
filename: job.filename || file.name,
|
||||
}
|
||||
|
||||
while (!["ready", "review", "failed"].includes(job.status)) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500))
|
||||
job = await admin.getTenantExport(importJobId)
|
||||
tenantImportProgress.value = {
|
||||
status: job.status,
|
||||
filesDone: job.filesDone || 0,
|
||||
filesTotal: job.filesTotal || 0,
|
||||
filename: job.filename || file.name,
|
||||
}
|
||||
}
|
||||
|
||||
if (job.status === "failed") {
|
||||
throw new Error(job.error || "Import konnte nicht abgeschlossen werden.")
|
||||
}
|
||||
|
||||
if (job.status === "review") {
|
||||
await router.push(`/administration/tenant-imports/${importJobId}`)
|
||||
return
|
||||
}
|
||||
|
||||
await fetchTenant()
|
||||
await auth.fetchMe()
|
||||
await auth.switchTenant(String(job.tenantId || targetTenantId))
|
||||
|
||||
toast.add({
|
||||
title: "Mandantenimport abgeschlossen",
|
||||
description: job.filename || file.name,
|
||||
color: "green",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const rowCount = (result.tables || []).reduce((sum, table) => sum + table.rows, 0)
|
||||
|
||||
lastImportResult.value = {
|
||||
tenantId: result.tenantId,
|
||||
tableCount: result.tables?.length || 0,
|
||||
rowCount,
|
||||
restoredFiles: result.files?.restored || 0,
|
||||
skippedFiles: result.files?.skipped || 0,
|
||||
}
|
||||
|
||||
await fetchTenant()
|
||||
await auth.fetchMe()
|
||||
await auth.switchTenant(String(result.tenantId))
|
||||
|
||||
toast.add({
|
||||
title: "Mandantenimport abgeschlossen",
|
||||
description: `${rowCount} Datensätze und ${lastImportResult.value.restoredFiles} Dateien verarbeitet.`,
|
||||
color: "green",
|
||||
})
|
||||
} catch (err: any) {
|
||||
console.error("[administration/tenants/import]", err)
|
||||
toast.add({
|
||||
title: "Mandant konnte nicht importiert werden",
|
||||
description: err?.data?.error || err?.message || "Unbekannter Fehler",
|
||||
color: "red",
|
||||
})
|
||||
} finally {
|
||||
importingTenant.value = false
|
||||
input.value = ""
|
||||
}
|
||||
}
|
||||
|
||||
const createTenantUser = async () => {
|
||||
if (!tenantForm.value || creatingUser.value) return
|
||||
|
||||
@@ -398,7 +278,7 @@ onMounted(async () => {
|
||||
<UCard v-if="!loading && tenantForm" class="mt-3">
|
||||
<USeparator label="Backup und Umzug" />
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<div class="mt-4 max-w-2xl">
|
||||
<div class="border border-gray-200 dark:border-gray-800 rounded-lg p-4">
|
||||
<div class="font-medium">Full Export</div>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
@@ -424,50 +304,7 @@ onMounted(async () => {
|
||||
Export herunterladen
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div class="border border-gray-200 dark:border-gray-800 rounded-lg p-4">
|
||||
<div class="font-medium">Import</div>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
Spielt einen FEDEO-Mandantenexport auf diesem Server ein. Bestehende Datensätze mit gleicher ID werden übersprungen.
|
||||
</p>
|
||||
<input
|
||||
ref="importFileInput"
|
||||
type="file"
|
||||
accept="application/json,application/zip,.json,.zip,.fedeo-export.zip"
|
||||
class="hidden"
|
||||
@change="importTenantExport"
|
||||
>
|
||||
<UButton
|
||||
class="mt-4"
|
||||
icon="i-heroicons-arrow-up-tray"
|
||||
color="warning"
|
||||
:loading="importingTenant"
|
||||
@click="openImportFileDialog"
|
||||
>
|
||||
Export importieren
|
||||
</UButton>
|
||||
<div v-if="tenantImportProgress" class="mt-4 space-y-2">
|
||||
<UProgress
|
||||
:model-value="tenantImportProgress.filesTotal ? Math.round((tenantImportProgress.filesDone / tenantImportProgress.filesTotal) * 100) : undefined"
|
||||
/>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ tenantImportProgress.status === 'ready' ? 'Import abgeschlossen' : 'Import wird verarbeitet' }}
|
||||
<span v-if="tenantImportProgress.filesTotal">
|
||||
· {{ tenantImportProgress.filesDone }} / {{ tenantImportProgress.filesTotal }} Schritte
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
v-if="lastImportResult"
|
||||
class="mt-4"
|
||||
title="Letzter Import"
|
||||
:description="`Tenant ${lastImportResult.tenantId}: ${lastImportResult.rowCount} Datensätze aus ${lastImportResult.tableCount} Tabellen verarbeitet, ${lastImportResult.restoredFiles} Dateien wiederhergestellt, ${lastImportResult.skippedFiles} Dateien übersprungen.`"
|
||||
color="green"
|
||||
variant="soft"
|
||||
/>
|
||||
</UCard>
|
||||
|
||||
<UCard v-if="!loading && tenantForm" class="mt-3">
|
||||
|
||||
Reference in New Issue
Block a user