149 lines
4.1 KiB
TypeScript
149 lines
4.1 KiB
TypeScript
import { ObjectStoreBuckets } from "../../constants"
|
|
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
|
import {
|
|
FieldType,
|
|
RenameColumn,
|
|
Row,
|
|
RowAttachment,
|
|
Table,
|
|
} from "@budibase/types"
|
|
|
|
export class AttachmentCleanup {
|
|
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
|
const appId = context.getAppId()
|
|
if (!dbCore.isProdAppID(appId)) {
|
|
const prodAppId = dbCore.getProdAppID(appId!)
|
|
// if prod exists, then don't allow deleting
|
|
const exists = await dbCore.dbExists(prodAppId)
|
|
if (exists) {
|
|
return
|
|
}
|
|
}
|
|
const files = fileListFn()
|
|
if (files.length > 0) {
|
|
await objectStore.deleteFiles(ObjectStoreBuckets.APPS, files)
|
|
}
|
|
}
|
|
|
|
private static extractAttachmentKeys(
|
|
type: FieldType,
|
|
rowData: RowAttachment[] | RowAttachment
|
|
): string[] {
|
|
if (
|
|
type !== FieldType.ATTACHMENTS &&
|
|
type !== FieldType.ATTACHMENT_SINGLE &&
|
|
type !== FieldType.SIGNATURE_SINGLE
|
|
) {
|
|
return []
|
|
}
|
|
|
|
if (!rowData) {
|
|
return []
|
|
}
|
|
|
|
if (type === FieldType.ATTACHMENTS && Array.isArray(rowData)) {
|
|
return rowData
|
|
.filter(attachment => attachment.key)
|
|
.map(attachment => attachment.key!)
|
|
} else if ("key" in rowData && rowData.key) {
|
|
return [rowData.key]
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
private static async tableChange(
|
|
table: Table,
|
|
rows: Row[],
|
|
opts: { oldTable?: Table; rename?: RenameColumn; deleting?: boolean }
|
|
) {
|
|
return AttachmentCleanup.coreCleanup(() => {
|
|
let files: string[] = []
|
|
const tableSchema = opts.oldTable?.schema || table.schema
|
|
for (let [key, schema] of Object.entries(tableSchema)) {
|
|
if (
|
|
schema.type !== FieldType.ATTACHMENTS &&
|
|
schema.type !== FieldType.ATTACHMENT_SINGLE &&
|
|
schema.type !== FieldType.SIGNATURE_SINGLE
|
|
) {
|
|
continue
|
|
}
|
|
|
|
const columnRemoved = opts.oldTable && !table.schema[key]
|
|
const renaming = opts.rename?.old === key
|
|
// old table had this column, new table doesn't - delete it
|
|
if ((columnRemoved && !renaming) || opts.deleting) {
|
|
rows.forEach(row => {
|
|
files = files.concat(
|
|
AttachmentCleanup.extractAttachmentKeys(schema.type, row[key])
|
|
)
|
|
})
|
|
}
|
|
}
|
|
return files
|
|
})
|
|
}
|
|
|
|
static async tableDelete(table: Table, rows: Row[]) {
|
|
return AttachmentCleanup.tableChange(table, rows, { deleting: true })
|
|
}
|
|
|
|
static async tableUpdate(
|
|
table: Table,
|
|
rows: Row[],
|
|
opts: { oldTable?: Table; rename?: RenameColumn }
|
|
) {
|
|
return AttachmentCleanup.tableChange(table, rows, opts)
|
|
}
|
|
|
|
static async rowDelete(table: Table, rows: Row[]) {
|
|
return AttachmentCleanup.coreCleanup(() => {
|
|
let files: string[] = []
|
|
for (let [key, schema] of Object.entries(table.schema)) {
|
|
if (
|
|
schema.type !== FieldType.ATTACHMENTS &&
|
|
schema.type !== FieldType.ATTACHMENT_SINGLE &&
|
|
schema.type !== FieldType.SIGNATURE_SINGLE
|
|
) {
|
|
continue
|
|
}
|
|
|
|
rows.forEach(row => {
|
|
files = files.concat(
|
|
AttachmentCleanup.extractAttachmentKeys(schema.type, row[key])
|
|
)
|
|
})
|
|
}
|
|
return files
|
|
})
|
|
}
|
|
|
|
static rowUpdate(table: Table, opts: { row: Row; oldRow: Row }) {
|
|
return AttachmentCleanup.coreCleanup(() => {
|
|
let files: string[] = []
|
|
for (let [key, schema] of Object.entries(table.schema)) {
|
|
if (
|
|
schema.type !== FieldType.ATTACHMENTS &&
|
|
schema.type !== FieldType.ATTACHMENT_SINGLE &&
|
|
schema.type !== FieldType.SIGNATURE_SINGLE
|
|
) {
|
|
continue
|
|
}
|
|
|
|
const oldKeys = AttachmentCleanup.extractAttachmentKeys(
|
|
schema.type,
|
|
opts.oldRow[key]
|
|
)
|
|
const newKeys = AttachmentCleanup.extractAttachmentKeys(
|
|
schema.type,
|
|
opts.row[key]
|
|
)
|
|
files = files.concat(
|
|
oldKeys.filter((key: string) => newKeys.indexOf(key) === -1)
|
|
)
|
|
}
|
|
return files
|
|
})
|
|
}
|
|
}
|