fix(event_file): use for_type/for_id for hosted_file_link deletion

hosted_file_link is created against the parent object (e.g. event_presenter),
not against event_file itself. The delete endpoint was passing link_to_type=
'event_file', finding 0 rows, and bailing before the orphan cleanup ran.

Fix: read for_type + for_id from the event_file row and use those for link
deletion. Also inline the orphan cleanup so a missing link (old orphan from
the pre-fix period) is treated as a non-fatal warning rather than a hard
failure — cleanup proceeds regardless.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-18 18:27:19 -04:00
parent 02a91a8ee3
commit 45b2890e27

View File

@@ -14,7 +14,7 @@ from app.config import settings
from app.db_sql import redis_lookup_id_random, sql_select, sql_update, sql_delete, get_id_random
from app.methods.hosted_file_methods import (
create_hosted_file_obj, load_hosted_file_obj, save_file,
create_hosted_file_link, delete_hosted_file_link, handle_delete_hosted_file
create_hosted_file_link, delete_hosted_file_link, get_hosted_file_link_rec_list
)
from app.methods.event_file_methods import create_event_file_obj, load_event_file_obj
from app.lib_general_v3 import (
@@ -296,26 +296,63 @@ async def delete_event_file_action(
if not ef_id_int:
raise HTTPException(status_code=404, detail="Event file not found.")
ef_rec = sql_select(sql="SELECT hosted_file_id FROM event_file WHERE id = :id", data={'id': ef_id_int})
# Fetch hosted_file_id + the parent the link was originally created against.
# hosted_file_link uses for_type/for_id as link target, NOT 'event_file'.
ef_rec = sql_select(
sql="SELECT hosted_file_id, for_type, for_id FROM event_file WHERE id = :id",
data={'id': ef_id_int}
)
hf_id_int = ef_rec.get('hosted_file_id') if ef_rec else None
for_type = ef_rec.get('for_type') if ef_rec else None
for_id_int = ef_rec.get('for_id') if ef_rec else None
link_cleaned = False
if hf_id_int:
link_cleaned = handle_delete_hosted_file(
account_id=account.account_id,
hosted_file_id=hf_id_int,
link_to_type='event_file',
link_to_id=ef_id_int,
rm_orphan=rm_orphan,
)
if not link_cleaned:
log.warning(f"handle_delete_hosted_file returned False for hosted_file {hf_id_int} / event_file {ef_id_int}")
orphan_cleaned = False
if hf_id_int:
# Step 1: Remove the hosted_file_link using the correct parent type/id.
# A missing link (old orphan from pre-fix period) is non-fatal — log and continue.
if for_type and for_id_int:
link_result = delete_hosted_file_link(
account_id=account.account_id,
hosted_file_id=hf_id_int,
link_to_type=for_type,
link_to_id=for_id_int,
)
if link_result:
link_cleaned = True
else:
log.warning(f"hosted_file_link not found (already removed or never created): hosted_file={hf_id_int} {for_type}:{for_id_int}")
# Step 2: Orphan check — clean up physical file + hosted_file record if no links remain.
if rm_orphan:
remaining = get_hosted_file_link_rec_list(hosted_file_id=hf_id_int)
if not remaining:
hf_obj = load_hosted_file_obj(hosted_file_id=hf_id_int)
if hf_obj:
file_path = os.path.join(
settings.FILES_PATH['hosted_files_root'],
hf_obj.subdirectory_path or '',
f'{hf_obj.hash_sha256}.file'
)
if os.path.exists(file_path):
try:
pathlib.Path(file_path).unlink()
log.info(f"Deleted physical file: {file_path}")
except OSError as e:
log.error(f"Failed to delete physical file {file_path}: {e}")
else:
log.warning(f"Physical file already absent from disk: {file_path}")
sql_delete(table_name='hosted_file', record_id=hf_id_int)
orphan_cleaned = True
# Step 3: Always remove the event_file row.
sql_delete(table_name='event_file', record_id=ef_id_int)
return mk_resp(data={
'event_file_deleted': True,
'hosted_file_link_cleaned': bool(link_cleaned),
'hosted_file_link_cleaned': link_cleaned,
'orphan_cleaned': orphan_cleaned,
})