fix(files): use new backend action endpoints for event_file delete + orphan scan
DELETE /v3/action/event_file/{id} now handles full atomic cleanup (link
removal, physical file, hosted_file record) in one call — replaces the
multi-step Redis pre-warm workaround. orphan_scan endpoint replaces the
N+1 per-file /links fetch on the admin files page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -365,70 +365,22 @@ export async function create_event_file_obj_from_hosted_file_async({
|
|||||||
export async function delete_ae_obj_id__event_file({
|
export async function delete_ae_obj_id__event_file({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
event_file_id,
|
event_file_id,
|
||||||
hosted_file_id,
|
|
||||||
params = {},
|
params = {},
|
||||||
try_cache = true,
|
try_cache = true,
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: {
|
}: {
|
||||||
api_cfg: any;
|
api_cfg: any;
|
||||||
event_file_id: string;
|
event_file_id: string;
|
||||||
// Providing hosted_file_id enables full cleanup of the physical file and
|
|
||||||
// hosted_file record. The /links fetch is done first because it calls
|
|
||||||
// get_id_random() on each linked object, which populates Redis. Without
|
|
||||||
// this pre-fetch, redis_lookup_id_random('event_file', id) raises 404
|
|
||||||
// in the delete handler — the cleanup is silently skipped.
|
|
||||||
hosted_file_id?: string;
|
|
||||||
params?: key_val;
|
params?: key_val;
|
||||||
try_cache?: boolean;
|
try_cache?: boolean;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}) {
|
}) {
|
||||||
if (hosted_file_id) {
|
// Single atomic call: removes hosted_file_link, cleans up physical file +
|
||||||
// Step 1: Fetch links — populates Redis for all linked object IDs.
|
// hosted_file record if no other links remain (rm_orphan=true is the default),
|
||||||
let file_links: { link_to_type: string; link_to_id_random: string | null }[] = [];
|
// then deletes the event_file row. Replaces the old multi-step workaround.
|
||||||
try {
|
const result = await api.delete_object({
|
||||||
const links_result = await api.get_object({
|
|
||||||
api_cfg,
|
api_cfg,
|
||||||
endpoint: `/v3/action/hosted_file/${hosted_file_id}/links`,
|
endpoint: `/v3/action/event_file/${event_file_id}`,
|
||||||
log_lvl: 0
|
|
||||||
});
|
|
||||||
file_links = links_result?.data ?? links_result ?? [];
|
|
||||||
} catch (e) {
|
|
||||||
if (log_lvl) console.warn('[delete_event_file] /links fetch failed:', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Remove each link. rm_orphan=true on the last one triggers
|
|
||||||
// physical file + hosted_file record cleanup if nothing else uses it.
|
|
||||||
if (file_links.length > 0) {
|
|
||||||
for (let i = 0; i < file_links.length; i++) {
|
|
||||||
const lnk = file_links[i];
|
|
||||||
const is_last = i === file_links.length - 1;
|
|
||||||
await api.delete_hosted_file({
|
|
||||||
api_cfg,
|
|
||||||
hosted_file_id,
|
|
||||||
link_to_type: lnk.link_to_type,
|
|
||||||
link_to_id: lnk.link_to_id_random ?? undefined,
|
|
||||||
rm_orphan: is_last,
|
|
||||||
params: { method: 'delete' },
|
|
||||||
log_lvl
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No links recorded — attempt direct orphan cleanup.
|
|
||||||
await api.delete_hosted_file({
|
|
||||||
api_cfg,
|
|
||||||
hosted_file_id,
|
|
||||||
rm_orphan: true,
|
|
||||||
params: { method: 'delete' },
|
|
||||||
log_lvl
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Delete the event_file record after the hosted_file cleanup.
|
|
||||||
const result = await api.delete_ae_obj({
|
|
||||||
api_cfg,
|
|
||||||
obj_type: 'event_file',
|
|
||||||
obj_id: event_file_id,
|
|
||||||
params,
|
params,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -570,8 +570,6 @@ async function handle_convert_pdf_to_image(event_file_obj: key_val) {
|
|||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
event_file_id:
|
event_file_id:
|
||||||
event_file_obj.event_file_id,
|
event_file_obj.event_file_id,
|
||||||
hosted_file_id:
|
|
||||||
event_file_obj.hosted_file_id,
|
|
||||||
log_lvl: 1
|
log_lvl: 1
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -359,31 +359,30 @@ let displayed_results = $derived(
|
|||||||
async function check_all_for_orphans() {
|
async function check_all_for_orphans() {
|
||||||
orphan_checking = true;
|
orphan_checking = true;
|
||||||
orphan_filter = false;
|
orphan_filter = false;
|
||||||
// Fetch links for all visible results in parallel; skip already-cached entries.
|
|
||||||
await Promise.all(
|
|
||||||
results.map(async (file) => {
|
|
||||||
const id = file.hosted_file_id;
|
|
||||||
if (links_map.has(id)) return;
|
|
||||||
links_loading.set(id, true);
|
|
||||||
try {
|
try {
|
||||||
|
// Single backend query instead of N+1 per-file link fetches.
|
||||||
const result = await api.get_object({
|
const result = await api.get_object({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
endpoint: `/v3/action/hosted_file/${id}/links`,
|
endpoint: `/v3/action/hosted_file/orphan_scan?limit=500`,
|
||||||
log_lvl: 0
|
log_lvl: 0
|
||||||
});
|
});
|
||||||
links_map.set(id, result?.data ?? result ?? []);
|
const db_orphans: { hosted_file_id: string }[] = result?.db_orphans ?? result?.data?.db_orphans ?? [];
|
||||||
// Also resolve nav URLs for any links found
|
const orphan_id_set = new Set(db_orphans.map((o) => o.hosted_file_id));
|
||||||
await Promise.all((result?.data ?? result ?? []).map((lnk: any) => resolve_link_url(lnk.link_to_type, lnk.link_to_id_random)));
|
// Only mark confirmed orphans — empty array means no links found.
|
||||||
} catch {
|
// Non-orphans are left out of links_map entirely so the link toggle
|
||||||
links_map.set(id, []);
|
// still works normally and orphan_ids only captures confirmed orphans.
|
||||||
} finally {
|
for (const file of results) {
|
||||||
links_loading.delete(id);
|
if (orphan_id_set.has(file.hosted_file_id)) {
|
||||||
|
links_map.set(file.hosted_file_id, []);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
);
|
} catch (e) {
|
||||||
|
console.error('[orphan_scan]', e);
|
||||||
|
} finally {
|
||||||
orphan_filter = true;
|
orphan_filter = true;
|
||||||
orphan_checking = false;
|
orphan_checking = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user