diff --git a/app/routers/api_v3_actions_hosted_file.py b/app/routers/api_v3_actions_hosted_file.py index f698bd0..9d252d3 100644 --- a/app/routers/api_v3_actions_hosted_file.py +++ b/app/routers/api_v3_actions_hosted_file.py @@ -370,6 +370,37 @@ async def check_hosted_file_obj_w_hash_action( return mk_resp(data=False, status_code=404, response=response, status_message="No record found for this hash.") +@router.get('/{hosted_file_id}/links', response_model=Resp_Body_Base) +async def get_file_links_action( + hosted_file_id: str = Path(min_length=11, max_length=22), + account: AccountContext = Depends(get_account_context), + ): + """ + Return all hosted_file_link records for a file, with link_to_id_random resolved. + Used by admin file management to show what objects a file is linked to. + """ + file_id_int = redis_lookup_id_random(record_id_random=hosted_file_id, table_name='hosted_file') + if not file_id_int: + raise HTTPException(status_code=404, detail="Hosted file not found.") + + raw_links = get_hosted_file_link_rec_list(hosted_file_id=file_id_int) + if not raw_links: + return mk_resp(data=[]) + + links = [] + for link in raw_links: + link_to_type = link.get('link_to_type') + link_to_id_int = link.get('link_to_id') + link_to_id_random = get_id_random(record_id=link_to_id_int, table_name=link_to_type) if link_to_type and link_to_id_int else None + links.append({ + 'link_to_type': link_to_type, + 'link_to_id': link_to_id_random, + 'link_to_id_random': link_to_id_random, + }) + + return mk_resp(data=links) + + @router.delete('/{hosted_file_id}', response_model=Resp_Body_Base) async def delete_file_action( hosted_file_id: str = Path(min_length=11, max_length=22), diff --git a/documentation/GUIDE__AE_API_V3_for_Frontend.md b/documentation/GUIDE__AE_API_V3_for_Frontend.md index 2d9d439..dbaa8fa 100644 --- a/documentation/GUIDE__AE_API_V3_for_Frontend.md +++ b/documentation/GUIDE__AE_API_V3_for_Frontend.md @@ -1,5 +1,7 @@ # Aether API V3 Frontend Integration Guide (Svelte/TypeScript) +**Last Updated:** 2026-06-12 + This guide defines the standards for interacting with the **Aether API V3 CRUD** and **Action** endpoints. --- @@ -368,6 +370,20 @@ These helper endpoints let the frontend request small server-side transformation - Add `?background=true` to schedule the clip asynchronously — returns `202 Accepted` immediately; poll the `hosted_file` record for completion. - Returns 400 on synchronous failure; 202 when scheduled successfully. +- **Get Links** + - Method: `GET` + - Path: `/v3/action/hosted_file/{hosted_file_id}/links` + - Auth: standard V3 headers + - Returns: array of `{ link_to_type, link_to_id, link_to_id_random }` for every record in `hosted_file_link`. Empty array if no links exist (file is an orphan). + - Use this to assess what objects are using a file before deleting it. + +- **Delete** + - Method: `DELETE` + - Path: `/v3/action/hosted_file/{hosted_file_id}` + - Query params: `link_to_type`, `link_to_id` (random string), `method` (`hide` | `disable` | `delete`, default `hide`), `rm_orphan` (bool, default `false`) + - Behavior: removes the specified link record, then if `rm_orphan=true` and no links remain, applies `method` to the file. Use `method=delete` to hard-delete the physical file and DB record. Without `link_to_type`/`link_to_id`, no link is removed; `rm_orphan` only fires if the file already has zero links. + - Use the `/links` endpoint first to get `link_to_id_random` — the delete endpoint resolves `link_to_id` as a random string, not an integer. + Frontend guidance: - Call these routes with the same `link_to_type` / `link_to_id` you plan to associate the resulting hosted_file with — the server resolves random IDs for you.