KI-AGENT: Serialisiere JSON-Felder beim Mandantenimport
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 17s
Build and Push Docker Images / build-frontend (push) Successful in 11s
Build and Push Docker Images / build-docs (push) Successful in 10s

This commit is contained in:
2026-05-18 21:35:16 +02:00
parent bb3b842be1
commit ff70338b21

View File

@@ -6,6 +6,10 @@ import { s3 } from "./s3"
import { secrets } from "./secrets"
type TableRows = Record<string, Record<string, any>[]>
type TableMetadata = {
columns: string[]
jsonColumns: Set<string>
}
export type TenantFullExport = {
format: "fedeo.tenant-full-export"
@@ -33,17 +37,25 @@ const quoteIdent = (value: string) => `"${value.replace(/"/g, '""')}"`
const tableColumns = async (client: any) => {
const result = await client.query(`
select table_name, column_name
select table_name, column_name, data_type
from information_schema.columns
where table_schema = 'public'
order by table_name, ordinal_position
`)
const columnsByTable = new Map<string, string[]>()
const columnsByTable = new Map<string, TableMetadata>()
for (const row of result.rows) {
const columns = columnsByTable.get(row.table_name) || []
columns.push(row.column_name)
columnsByTable.set(row.table_name, columns)
const metadata = columnsByTable.get(row.table_name) || {
columns: [],
jsonColumns: new Set<string>(),
}
metadata.columns.push(row.column_name)
if (row.data_type === "json" || row.data_type === "jsonb") {
metadata.jsonColumns.add(row.column_name)
}
columnsByTable.set(row.table_name, metadata)
}
return columnsByTable
@@ -103,8 +115,9 @@ export const buildTenantFullExport = async (server: FastifyInstance, tenantId: n
addRows(tables, "tenants", tenantRows)
for (const [table, columns] of columnsByTable.entries()) {
for (const [table, metadata] of columnsByTable.entries()) {
if (table === "tenants") continue
const { columns } = metadata
const tenantColumn = columns.includes("tenant")
? "tenant"
@@ -196,18 +209,25 @@ const restoreFiles = async (exportData: TenantFullExport) => {
return { restored, skipped }
}
const insertRows = async (client: any, table: string, rows: Record<string, any>[], columns: string[]) => {
const prepareColumnValue = (value: any, isJsonColumn: boolean) => {
if (!isJsonColumn || value === null || typeof value === "undefined") return value
if (typeof value === "string") return value
return JSON.stringify(value)
}
const insertRows = async (client: any, table: string, rows: Record<string, any>[], metadata: TableMetadata) => {
if (!rows.length) return 0
let inserted = 0
const availableColumns = new Set(columns)
const availableColumns = new Set(metadata.columns)
for (const row of rows) {
const rowColumns = Object.keys(row).filter((column) => availableColumns.has(column))
if (!rowColumns.length) continue
const placeholders = rowColumns.map((_, index) => `$${index + 1}`).join(", ")
const values = rowColumns.map((column) => row[column])
const values = rowColumns.map((column) => prepareColumnValue(row[column], metadata.jsonColumns.has(column)))
await client.query(
`insert into ${quoteIdent(table)} (${rowColumns.map(quoteIdent).join(", ")}) values (${placeholders}) on conflict do nothing`,
@@ -219,8 +239,9 @@ const insertRows = async (client: any, table: string, rows: Record<string, any>[
return inserted
}
const refreshSequences = async (client: any, columnsByTable: Map<string, string[]>) => {
for (const [table, columns] of columnsByTable.entries()) {
const refreshSequences = async (client: any, columnsByTable: Map<string, TableMetadata>) => {
for (const [table, metadata] of columnsByTable.entries()) {
const { columns } = metadata
if (!columns.includes("id")) continue
const sequenceResult = await client.query("select pg_get_serial_sequence($1, $2) as sequence_name", [`public.${table}`, "id"])
@@ -269,10 +290,10 @@ export const importTenantFullExport = async (server: FastifyInstance, exportData
const importedTables: { table: string; rows: number }[] = []
for (const table of tableNames) {
const rows = exportData.tables[table] || []
const columns = columnsByTable.get(table)
if (!columns) continue
const metadata = columnsByTable.get(table)
if (!metadata) continue
const count = await insertRows(client, table, rows, columns)
const count = await insertRows(client, table, rows, metadata)
importedTables.push({ table, rows: count })
}