82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
export const WARNING_LIMIT_BYTES = 4_294_967_296;
|
|
export const CRITICAL_LIMIT_BYTES = 4_831_838_208;
|
|
export const HARD_LIMIT_BYTES = 5_368_709_120;
|
|
export const CAPACITY_COUNTED_FILE_KINDS = [
|
|
"reference",
|
|
"generated",
|
|
"export",
|
|
"derived",
|
|
"sticker_original",
|
|
"sticker_thumbnail",
|
|
] as const;
|
|
export const CAPACITY_EXCLUDED_STORAGE = [
|
|
"read_only_assets",
|
|
"application_files",
|
|
"database",
|
|
"logs",
|
|
"audit",
|
|
"browser_cache",
|
|
"user_downloads",
|
|
] as const;
|
|
|
|
export type CapacityNoticeLevel = "normal" | "warning" | "critical";
|
|
export type StorageStatus = "active" | "full" | "unavailable";
|
|
export type StorageAction =
|
|
| "project_json_write"
|
|
| "binary_write"
|
|
| "ai_call"
|
|
| "latest_export_write"
|
|
| "read"
|
|
| "download"
|
|
| "client_only_download"
|
|
| "permanent_delete"
|
|
| "explicit_cleanup";
|
|
export type StorageActionDecision = "allow" | "reject_capacity" | "reject_unavailable" | "reject_uncommitted";
|
|
|
|
export function classifyCapacity(managedContentBytes: number, activeReservationBytes: number) {
|
|
if (!Number.isSafeInteger(managedContentBytes) || managedContentBytes < 0) throw new Error("managed_content_bytes_invalid");
|
|
if (!Number.isSafeInteger(activeReservationBytes) || activeReservationBytes < 0) throw new Error("active_reservation_bytes_invalid");
|
|
const capacity_notice_level: CapacityNoticeLevel = managedContentBytes < WARNING_LIMIT_BYTES
|
|
? "normal"
|
|
: managedContentBytes < CRITICAL_LIMIT_BYTES
|
|
? "warning"
|
|
: "critical";
|
|
const storage_status: StorageStatus = managedContentBytes + activeReservationBytes >= HARD_LIMIT_BYTES
|
|
? "full"
|
|
: "active";
|
|
return { capacity_notice_level, storage_status };
|
|
}
|
|
|
|
export function decideStorageAction(
|
|
status: StorageStatus,
|
|
sqliteWritable: boolean,
|
|
action: StorageAction,
|
|
): StorageActionDecision {
|
|
if (status === "unavailable") {
|
|
if (action === "read" || action === "download" || action === "client_only_download") return "allow";
|
|
if (action === "permanent_delete" || action === "explicit_cleanup") {
|
|
return sqliteWritable ? "allow" : "reject_uncommitted";
|
|
}
|
|
return "reject_unavailable";
|
|
}
|
|
if (status === "full" && (action === "binary_write" || action === "ai_call" || action === "latest_export_write")) {
|
|
return "reject_capacity";
|
|
}
|
|
return "allow";
|
|
}
|
|
|
|
export function storageCapacityErrorDetails(input: {
|
|
activeReservationBytes: number;
|
|
managedContentBytes: number;
|
|
projectedWriteBytes: number;
|
|
}) {
|
|
const { capacity_notice_level, storage_status } = classifyCapacity(
|
|
input.managedContentBytes,
|
|
input.activeReservationBytes,
|
|
);
|
|
return {
|
|
capacity_status: storage_status === "full" ? "full" as const : capacity_notice_level,
|
|
remaining_bytes: Math.max(0, HARD_LIMIT_BYTES - input.managedContentBytes - input.activeReservationBytes),
|
|
};
|
|
}
|