Add bots files handling logic
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { BUN_SQLITE_COBBLE_BOT_FILES } from "@/db/types/db";
|
||||
import { rmSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { BotFileScope } from "@/src/dict/bot-file-dict";
|
||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||
import { logError } from "@/src/functions/backend/logger";
|
||||
import grabStorageDir from "./grab-storage-dir";
|
||||
|
||||
type Params = {
|
||||
scope: BotFileScope;
|
||||
owner_id: number | string;
|
||||
/**
|
||||
* Its files' `media` ids, read before the rows go. Left out, they are
|
||||
* read here (only useful when the catalog rows still exist).
|
||||
*/
|
||||
media_ids?: number[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The `media` ids of a directory's files, read before the bot or workspace
|
||||
* (and with it, the catalog rows) is deleted.
|
||||
*/
|
||||
export async function grabStorageMediaIds({ scope, owner_id }: { scope: BotFileScope; owner_id: number | string }) {
|
||||
const res = await BunSQLite.sql<BUN_SQLITE_COBBLE_BOT_FILES>({
|
||||
sql: `SELECT media_id FROM bot_files WHERE scope = ? AND owner_id = ?`,
|
||||
values: [scope, owner_id],
|
||||
});
|
||||
|
||||
return (res.payload || []).map((file) => Number(file.media_id)).filter((id) => id > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory's files for good when the bot or workspace it
|
||||
* belongs to goes: the disk folder, every file's `media` row, and what is
|
||||
* left of the catalog. A failure is logged, never thrown: deleting the
|
||||
* bot must not stop on a stray file.
|
||||
*/
|
||||
export default async function purgeStorage({ scope, owner_id, media_ids }: Params) {
|
||||
try {
|
||||
const ids = media_ids || (await grabStorageMediaIds({ scope, owner_id }));
|
||||
|
||||
if (ids.length) {
|
||||
const placeholders = ids.map(() => "?").join(", ");
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `DELETE FROM media_paradigms WHERE media_id IN (${placeholders})`,
|
||||
values: ids,
|
||||
}).catch(() => {});
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `DELETE FROM media WHERE id IN (${placeholders})`,
|
||||
values: ids,
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `DELETE FROM bot_file_vectors WHERE scope = ? AND owner_id = ?`,
|
||||
values: [scope, owner_id],
|
||||
}).catch(() => {});
|
||||
|
||||
/**
|
||||
* Usually gone already, with the bot or workspace (the cascade).
|
||||
*/
|
||||
await BunSQLite.sql({
|
||||
sql: `DELETE FROM bot_files WHERE scope = ? AND owner_id = ?`,
|
||||
values: [scope, owner_id],
|
||||
});
|
||||
|
||||
const dir = grabStorageDir({ scope, owner_id, create: false });
|
||||
const { DATA_DIR } = grabDirNames();
|
||||
|
||||
/**
|
||||
* Never outside the data volume, whatever the path says.
|
||||
*/
|
||||
if (path.resolve(dir).startsWith(path.resolve(DATA_DIR))) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
} catch (error) {
|
||||
await logError({
|
||||
title: `${scope} ${owner_id}'s files couldn't be cleaned up`,
|
||||
error,
|
||||
metadata: { scope, owner_id },
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user