Valider fase 11: fiks size-inkonsistens og UTF-8-trunkering i RSS

Fase 11 (produksjon) validert — LiveKit, pruning og podcast-RSS:

- rss.rs + synops-rss: Les filstørrelse fra både 'size_bytes' (intentions)
  og 'size' (publishing) med COALESCE — forhindrer manglende enclosure-
  størrelse i podcast-feeds avhengig av opplastingsmetode.
- pruning.rs + synops-prune: Samme COALESCE-fix for konsistent size-tracking.
- rss.rs + synops-rss: Fiks truncate_description til å bruke char-indeksering
  istedenfor byte-indeksering — forhindrer panic på norsk tekst (å, ø, æ).

LiveKit kjører i Docker (healthy), token-generering via join_communication,
pruning-loop aktiv, RSS-endepunkt returnerer korrekt 404 for ukjent slug.
Alle 61 maskinrommet-tester bestått.
This commit is contained in:
vegard 2026-03-18 15:15:08 +00:00
parent 2ccbebe222
commit a836be6992
5 changed files with 20 additions and 16 deletions

View file

@ -222,7 +222,7 @@ async fn prune_by_ttl(
WHEN n.metadata->>'mime' LIKE 'video/%' THEN 'video'
ELSE 'other'
END AS mime_category,
COALESCE((n.metadata->>'size_bytes')::bigint, 0) AS size_bytes,
COALESCE((n.metadata->>'size_bytes')::bigint, (n.metadata->>'size')::bigint, 0) AS size_bytes,
n.created_at,
n.last_accessed_at,
EXISTS(

View file

@ -193,7 +193,7 @@ async fn fetch_feed_items(
e.metadata,
m.metadata->>'cas_hash' AS cas_hash,
m.metadata->>'mime' AS mime,
(m.metadata->>'size')::bigint AS size
COALESCE((m.metadata->>'size_bytes')::bigint, (m.metadata->>'size')::bigint) AS size
FROM edges e
JOIN nodes n ON n.id = e.source_id
LEFT JOIN edges me ON me.source_id = n.id AND me.edge_type = 'has_media'
@ -476,13 +476,16 @@ fn short_id(id: Uuid) -> String {
id.to_string()[..8].to_string()
}
/// Trunkér beskrivelse til maks antall tegn, på ordgrense.
fn truncate_description(s: &str, max_len: usize) -> String {
if s.len() <= max_len {
/// Trunkér beskrivelse til maks antall tegn (chars, ikke bytes), på ordgrense.
fn truncate_description(s: &str, max_chars: usize) -> String {
let char_count = s.chars().count();
if char_count <= max_chars {
return s.to_string();
}
match s[..max_len].rfind(' ') {
// Finn byte-posisjon for max_chars tegn
let byte_end = s.char_indices().nth(max_chars).map(|(i, _)| i).unwrap_or(s.len());
match s[..byte_end].rfind(' ') {
Some(pos) => format!("{}", &s[..pos]),
None => format!("{}", &s[..max_len]),
None => format!("{}", &s[..byte_end]),
}
}

View file

@ -299,8 +299,7 @@ med spesifikasjon for det som trenger en dedikert sesjon.
- [x] 23.2 Valider fase 34 (frontend + tilgang): SvelteKit-oppsett, OIDC-flow, sanntid, mottaksflate, TipTap-editor, node_access-matrise, team-transitivitet, visibility-filtrering.
- [x] 23.3 Valider fase 58 (kommunikasjon + CAS + lyd + aliaser): chat-loop, kontekst-arv, CAS-hashing/deduplisering, Whisper-pipeline, segmenttabell, SRT-eksport, alias-identitet.
- [x] 23.4 Valider fase 910 (visninger + AI): kanban drag-and-drop, kalender, dagbok, kunnskapsgraf, LiteLLM-ruting, AI-foreslåtte edges, oppsummering, TTS.
- [~] 23.5 Valider fase 11 (produksjon): LiveKit-oppsett, sanntidslyd, pruning-logikk, podcast-RSS.
> Påbegynt: 2026-03-18T15:10
- [x] 23.5 Valider fase 11 (produksjon): LiveKit-oppsett, sanntidslyd, pruning-logikk, podcast-RSS.
- [ ] 23.6 Valider fase 1314 (traits + publisering): trait-validering, pakkevelger, Tera-templates, HTML-rendering, forside, slot-håndtering, redaksjonell flyt, planlagt publisering, A/B-testing.
- [ ] 23.7 Valider fase 1516 (admin + lydmixer): systemvarsler, graceful shutdown, jobbkø-oversikt, ressursstyring, serverhelse, Web Audio mixer, delt kontroll, sound pads, EQ, stemmeeffekter.
- [ ] 23.8 Valider fase 1718 (lydstudio-utbedring + AI-verktøy): responsivt layout, FFmpeg-validering, fade/silence, AI-presets, direction-logikk, drag-and-drop integrasjon.

View file

@ -259,7 +259,7 @@ async fn phase_ttl(
WHEN n.metadata->>'mime' LIKE 'video/%' THEN 'video'
ELSE 'other'
END AS mime_category,
COALESCE((n.metadata->>'size_bytes')::bigint, 0) AS size_bytes,
COALESCE((n.metadata->>'size_bytes')::bigint, (n.metadata->>'size')::bigint, 0) AS size_bytes,
n.created_at,
n.last_accessed_at,
EXISTS(

View file

@ -268,7 +268,7 @@ async fn fetch_feed_items(
e.metadata,
m.metadata->>'cas_hash' AS cas_hash,
m.metadata->>'mime' AS mime,
(m.metadata->>'size')::bigint AS size
COALESCE((m.metadata->>'size_bytes')::bigint, (m.metadata->>'size')::bigint) AS size
FROM edges e
JOIN nodes n ON n.id = e.source_id
LEFT JOIN edges me ON me.source_id = n.id AND me.edge_type = 'has_media'
@ -562,13 +562,15 @@ fn short_id(id: Uuid) -> String {
id.to_string()[..8].to_string()
}
/// Trunkér beskrivelse til maks antall tegn, på ordgrense.
fn truncate_description(s: &str, max_len: usize) -> String {
if s.len() <= max_len {
/// Trunkér beskrivelse til maks antall tegn (chars, ikke bytes), på ordgrense.
fn truncate_description(s: &str, max_chars: usize) -> String {
let char_count = s.chars().count();
if char_count <= max_chars {
return s.to_string();
}
match s[..max_len].rfind(' ') {
let byte_end = s.char_indices().nth(max_chars).map(|(i, _)| i).unwrap_or(s.len());
match s[..byte_end].rfind(' ') {
Some(pos) => format!("{}", &s[..pos]),
None => format!("{}", &s[..max_len]),
None => format!("{}", &s[..byte_end]),
}
}