mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-22 03:05:32 +02:00
fix(core): delete_entry correctly nulls FK for child entries
archive_run_items.produced_entry_id has no ON DELETE action so it must be manually nulled before the entry row is deleted. The old query used WHERE root_entry_id = entry_id, which finds descendants of a root but returns nothing when entry_id IS a child (children have no sub-children, so no row has root_entry_id = child_id). The DELETE then failed under foreign_keys=ON. Fix: add OR produced_entry_id = ?1 so the entry's own run_item FK is always cleared before deletion, regardless of whether it is a root or a child. The subtree subquery is kept for the root-deletion case where all child run_items also need nulling.
This commit is contained in:
parent
c800a395c7
commit
d07e4d7ac9
3 changed files with 22 additions and 4 deletions
|
|
@ -16,6 +16,8 @@ Three crates with a strict ownership split — **core owns truth; CLI and server
|
|||
|
||||
Capture flow: locator → `determine_source()` (`crates/archivr-core/src/capture.rs`) routes by platform/shorthand (`yt:`, `x:`, `tweet:` …) → platform downloader (`downloader/ytdlp.rs`, `tweets.rs`, `singlefile.rs`, `http.rs`, `local.rs`) stages into `temp/` → SHA3-256 dedup (`hash.rs`, `downloader/store.rs`) moves blobs to `raw/A/B/HASH.EXT` → rows written to `archivr.sqlite` (runs, entries, artifacts, blobs) → served via `/api/archives/:id/...`. `CaptureConfig` carries per-request toggles (uBlock, reader mode, Freedium mirror, etc.); when `via_freedium` is set, the fetch URL is rewritten through `freedium-mirror.cfd` while the canonical DB URL stays the original locator.
|
||||
|
||||
YouTube playlists and channels produce a **parent container entry** with each video captured as a child entry. `downloader/ytdlp.rs` handles the playlist probe (fetching per-video quality metadata before archiving), the multi-video download loop, and sync mode (skipping already-archived videos when re-archiving a playlist or channel).
|
||||
|
||||
Per-archive layout (created by `archivr init`): `.archivr/` (name, store_path, `archivr.sqlite`) + sibling `store/` (`raw/`, `raw_tweets/`, `structured/`, `temp/`). Server-level auth lives in a **separate** `archivr-auth.sqlite` (users, sessions, API tokens, role bits GUEST=1/USER=2/ADMIN=4/OWNER=8).
|
||||
|
||||
The server mounts multiple archives from a TOML registry (`crates/archivr-server/src/registry.rs`); routes are parameterized by `:archive_id`.
|
||||
|
|
@ -78,6 +80,7 @@ No CI is configured; no rustfmt.toml/clippy.toml — default `cargo fmt`/`clippy
|
|||
- `crates/archivr-server/src/main.rs` — server bootstrap: config load, archive mounting, auth DB init, stalled-job recovery (running → failed on startup).
|
||||
- `crates/archivr-server/src/routes.rs` — all HTTP handlers and the router; grep here first for API work.
|
||||
- `crates/archivr-core/src/capture.rs` — `perform_capture()`, `Source` enum, shorthand parsing.
|
||||
- `crates/archivr-core/src/downloader/ytdlp.rs` — yt-dlp integration; YouTube playlist/channel probe and download, sync mode logic.
|
||||
- `crates/archivr-core/src/database.rs` — single source of truth for all SQLite schema and queries (both archive and auth DBs).
|
||||
- `frontend/src/App.jsx` / `frontend/src/api.js` — frontend root state and API surface.
|
||||
- `docker/config.example.toml` — server config schema: `bind`, `auth_db_path`, repeated `[[archives]]` (`id`, `label`, `archive_path`).
|
||||
|
|
|
|||
|
|
@ -64,6 +64,17 @@ label = "Personal"
|
|||
archive_path = "/path/to/archive/.archivr"
|
||||
```
|
||||
|
||||
## Entry Nesting
|
||||
|
||||
Entries support a two-level parent/child hierarchy. A **container entry** (playlist or channel) holds zero or more child entries (individual videos). Container entries have no primary media artifact of their own; their `total_artifact_bytes` is the sum of their children's bytes.
|
||||
|
||||
Rules:
|
||||
- Maximum nesting depth is 2 (root → child). Children cannot have children.
|
||||
- The UI shows child entries collapsed under their parent, expandable with a chevron.
|
||||
- Container entries are created by playlist/channel captures. Single-video and all other source types produce a standalone root entry with no children.
|
||||
|
||||
If a feature touches how entries are parented or how the UI groups them, start in `archivr-core` (`database.rs` for schema, `archive.rs` for listing, `capture.rs` for creation).
|
||||
|
||||
## How To Run It
|
||||
|
||||
There are two user-facing binaries:
|
||||
|
|
@ -155,6 +166,7 @@ sequenceDiagram
|
|||
| Capture orchestration, `Source` routing, `CaptureConfig` | `crates/archivr-core/src/capture.rs` |
|
||||
| Archive opening, listing entries, entry detail, runs | `crates/archivr-core/src/archive.rs` |
|
||||
| Download/save behavior | `crates/archivr-core/src/downloader/` |
|
||||
| YouTube playlist/channel download, playlist probe, sync mode | `crates/archivr-core/src/downloader/ytdlp.rs` and `capture.rs` |
|
||||
| CLI commands, argument parsing, terminal output | `crates/archivr-cli/src/main.rs` |
|
||||
| Server API routes | `crates/archivr-server/src/routes.rs` |
|
||||
| Auth model (users, sessions, tokens, roles) | `crates/archivr-server/src/auth.rs` |
|
||||
|
|
|
|||
|
|
@ -1638,10 +1638,13 @@ pub fn delete_entry(conn: &Connection, entry_uid: &str) -> Result<bool> {
|
|||
// shared blobs with any subtree member, excluding every subtree ID simultaneously.
|
||||
cascade_cached_bytes_after_subtree_delete(conn, &subtree_ids)?;
|
||||
|
||||
// Null the FK that has no ON DELETE action (covers root and all descendants).
|
||||
// Null the FK that has no ON DELETE action. Must cover:
|
||||
// - The entry itself (child entry: root_entry_id = playlist root, not self)
|
||||
// - All descendants (root entry: children have root_entry_id = entry_id)
|
||||
conn.execute(
|
||||
"UPDATE archive_run_items SET produced_entry_id = NULL
|
||||
WHERE produced_entry_id IN (
|
||||
WHERE produced_entry_id = ?1
|
||||
OR produced_entry_id IN (
|
||||
SELECT id FROM archived_entries WHERE root_entry_id = ?1
|
||||
)",
|
||||
[entry_id],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue