feat(analysis): live SSE event stream for the admin dashboard
Broadcasts analysis progress so the dashboard updates live: - analysis::events: AnalysisEvents broadcaster + AnalysisEvent (Enqueued / Started / Completed / Failed), carrying the manga/chapter/page breadcrumb. - The worker daemon resolves each page's breadcrumb (repo::page::locate) and publishes Started before dispatch and Completed/Failed after. - The admin reenqueue publishes Enqueued (scoped by manga/chapter). - GET /v1/admin/analysis/status/stream — SSE (RequireAdmin) forwarding each event as a named `analysis` frame; broadcast lag emits a `lagged` frame. AppState carries the always-present events bus. Tests: worker publishes started+completed (with breadcrumb) and failed; SSE route is admin-gated (403 non-admin) and returns text/event-stream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,22 @@ pub async fn find_by_id(pool: &PgPool, id: Uuid) -> AppResult<Option<Page>> {
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
/// Resolve a page's breadcrumb `(manga_id, chapter_id, page_number)` for
|
||||
/// live event payloads. `None` if the page doesn't exist.
|
||||
pub async fn locate(
|
||||
pool: &PgPool,
|
||||
page_id: Uuid,
|
||||
) -> AppResult<Option<(Uuid, Uuid, i32)>> {
|
||||
let row: Option<(Uuid, Uuid, i32)> = sqlx::query_as(
|
||||
"SELECT c.manga_id, p.chapter_id, p.page_number \
|
||||
FROM pages p JOIN chapters c ON c.id = p.chapter_id WHERE p.id = $1",
|
||||
)
|
||||
.bind(page_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
pub async fn list_for_chapter(pool: &PgPool, chapter_id: Uuid) -> AppResult<Vec<Page>> {
|
||||
let rows = sqlx::query_as::<_, Page>(
|
||||
r#"
|
||||
|
||||
Reference in New Issue
Block a user