Files
cobble/src/functions/backend/db/bot-files/move-bot-file.ts
T
2026-10-21 17:56:09 +01:00

85 lines
2.9 KiB
TypeScript

import BunSQLite from "@moduletrace/bun-sqlite";
import { existsSync, mkdirSync, renameSync, rmSync, copyFileSync } from "node:fs";
import path from "node:path";
import AppError from "@/src/utils/app-error";
import grabDirNames from "@/src/utils/grab-dir-names";
import resolveBotPath from "@/src/utils/resolve-bot-path";
import type { StorageScope } from "@/src/utils/storage-access";
import { canReachScope } from "@/src/utils/storage-access";
import grabBotFile from "./grab-bot-file";
import grabStorageDir from "./grab-storage-dir";
import { grabBotFileRow } from "./record-bot-file";
type Params = {
scopes: StorageScope[];
file_id: number | string;
/**
* The new path, in the file's own directory.
*/
path: string;
};
/**
* Renames a file or moves it into another folder, inside its own
* directory. The bytes move on disk and the row follows; an existing file
* at the target is refused, never overwritten by a rename.
*/
export default async function moveBotFile({ scopes, file_id, path: target }: Params) {
const file = await grabBotFile({ scopes, file_id });
if (!file?.id) {
throw new AppError(`That file isn't in your files.`);
}
if (!canReachScope({ scopes, scope: file.scope as any, owner_id: file.owner_id! })) {
throw new AppError(`That file isn't in your files.`);
}
const root = grabStorageDir({ scope: file.scope as any, owner_id: file.owner_id! });
const resolved = resolveBotPath({ root, path: target });
if (resolved.relative == file.path) return file;
const taken = await grabBotFileRow({
scope: file.scope as any,
owner_id: file.owner_id!,
path: resolved.relative,
});
if (taken?.id) {
throw new AppError(`There's already a file at ${resolved.relative}.`);
}
const { DATA_DIR } = grabDirNames();
const from = path.join(root, file.path || "");
mkdirSync(path.dirname(resolved.absolute), { recursive: true });
if (existsSync(from)) {
try {
renameSync(from, resolved.absolute);
} catch {
copyFileSync(from, resolved.absolute);
rmSync(from, { force: true });
}
}
await BunSQLite.sql({
sql: `UPDATE bot_files SET path = ?, name = ?, folder = ?, updated_at = ? WHERE id = ?`,
values: [resolved.relative, resolved.name, resolved.folder, Date.now(), file.id],
});
if (file.media_id) {
const write_path = resolved.absolute.startsWith(DATA_DIR)
? resolved.absolute.slice(DATA_DIR.length).replace(/^[\\/]/, "").split("\\").join("/")
: resolved.relative;
await BunSQLite.sql({
sql: `UPDATE media SET media_write_path = ?, updated_at = ? WHERE id = ?`,
values: [write_path, Date.now(), file.media_id],
});
}
return { ...file, path: resolved.relative, name: resolved.name, folder: resolved.folder };
}