Commit graph

3 commits

Author SHA1 Message Date
04b3712ef5 Planlagte sendinger
«Send denne kl. 18:00.» Engangs, ikke gjentakende.

Dette er den første delen av appen som må ligge på disk. Kø og historikk har
alltid bare levd i minnet, og det har gått bra fordi en melding spilles med en
gang. En melding planlagt til 18:00 må overleve at containeren startes om
17:45, så schedule.py lagrer i data/scheduled.json – skrevet til side og
flyttet på plass, så fila aldri er halvskrevet.

Ingen database, med vilje: dette er en håndfull meldinger på et arrangement,
og en fil man kan lese med cat er verdt mer her enn indekser. En ødelagt fil
hindrer ikke oppstart; da er det bedre å miste planene og si det i loggen.

Modulen vet ingenting om Flask eller avspilling. Den lagrer, og den svarer på
«hva skal sendes nå?». _scheduler() i announcer.py eier tråden som spør hvert
20. sekund og legger forfalte meldinger i den vanlige køen – derfra oppfører de
seg nøyaktig som en melding sendt for hånd.

Gikk den glipp av, spilles den ikke. due() skiller på grace_seconds (120):
innenfor den sendes meldingen, utenfor markeres den missed og vises som det.
Nådefristen er slingringsmonn for at tråden våkner med mellomrom og for en
kjapp omstart, ikke en policy om å ta igjen etterslep – «turneringen starter om
ti minutter» skal ikke ut på anlegget en time etter at maskina kom tilbake.
due() endrer ingenting selv; markeringen skjer først etter at meldingen
faktisk er i kø, så ingenting kan stå som «sendt» uten å ha blitt det.

Klienten sender absolutt tidspunkt (epoch), så tidssoner spiller ingen rolle
for selve planleggingen. TZ settes likevel fra .env, ellers står loggen i UTC
mens man selv står i CEST.

Planlegging er låst til samme forhåndslytt-nøkkel som «Send ut»: man planlegger
bare det man har hørt. Det gir en bonus – forhåndslytt varmer cache/, så WAV-en
ligger ferdig på disk, og en planlagt edge-tts-melding spiller kl. 18:00 selv
om nettet er nede da.

Sine egne kan man alltid avbryte. Andres krever clear, samme rett som å tømme
køen, siden det er samme slags inngrep i noe andre har satt i gang.

23 nye tester. Verifisert i container: sending fyrer og spiller, overlever en
omstart av containeren, og en melding som forfalt mens maskina var av blir
markert missed uten å bli spilt.

Merk ved oppgradering: schedule er en ny handling, og et oppsett med eksplisitt
[roles] i config.toml får den ikke automatisk. Den må legges inn selv.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 16:29:16 +02:00
6c1253f54c Docker er driftsformen, ikke bare en testrigg
Announcer skal kjøre standalone på klienten som er koblet til anlegget, i
Docker – aldri på utviklingsmaskina. install.sh installerte nativt med apt
og uv, og det passer ikke lenger.

- install.sh er nå en Docker-bootstrap: installerer Docker om den mangler,
  lager .env med riktig bruker-ID og portbinding, oppretter data/, bygger,
  starter, og setter ufw. Den rydder også bort gamle native enheter, både
  announcer.service og sg-announcer.service.
- Dockerfile kopierer koden inn istedenfor å låne den fra et bind-mount, så
  det som kjører i drift er uavhengig av hva som ligger i mappa.
- Alt som skal overleve en ny container ligger i data/ på verten. Bind-mount
  og ikke navngitt volum, med vilje: da kan man lese
  data/logs/announcements.jsonl og slippe egne jingler i data/chimes/ uten å
  gå inn i containeren, og eierskapet følger brukeren istedenfor root.
- Testriggen er flyttet til compose.test.yaml, med eget prosjektnavn, så den
  ikke kan forveksles med eller forstyrre drift.

Lyden ut av containeren krever tre ting, og de er dokumentert i AGENTS.md:
hele XDG_RUNTIME_DIR montert inn (ikke bare pipewire-socketen – PulseAudio
trenger pulse/native og en cookie i samme mappe, og da virker begge
lydservere), user: PUID:PGID lik eieren av socketen, og en innlogget grafisk
økt. Containeren starter ved boot, altså før innlogging, så avspilling feiler
til noen logger inn – det retter seg selv, siden player_cmd() slås opp per
melding. Verifisert: lyden spiller fra driftscontaineren.

Brannmuren er den viktige endringen fra nativ tid. Dockers publiserte porter
settes opp i nat-tabellen, før ufws regler, så en ufw-regel på porten gjør
ingenting. Tilgangen styres nå av BIND_IP i .env, og install.sh setter den til
NetBird-adressen når wt0 finnes. ufw settes fortsatt for ssh, men er ikke
involvert i tilgangen til announcer.

Testriggen bindes nå til 127.0.0.1. Den sto med PIN 1234 publisert på
0.0.0.0, og gikk altså forbi ufw ut på LAN-et.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 16:07:59 +02:00
6aaa98d8b1 Testoppsett i Docker med dummy-IdP
Crew-panelet har ikke OIDC ennå, så OIDC-veien kunne ikke prøves i det hele
tatt – bare enhetstestes mot et stubbet HTTP-lag. Dette gir noe å faktisk
logge inn i.

    docker compose up --build

    localhost:8091   announcer med PIN, JWT og OIDC slått på samtidig
    localhost:9000   docker/dummy_idp.py, som står for crew-panelet

8091 med vilje, så den ikke kolliderer med tjenesten på 8090.

Dummy-IdP-en kjører en ekte autorisasjonskode-runde: den krever PKCE med
S256, avviser feil code_verifier, bruker koden bare én gang, og signerer
id_token med klienthemmeligheten. Du velger hvem du logger inn som – Kari
(crew), Kent (admin) eller Ola, som har null roller og dermed skal gi 403
og ikke 401. /token?who=kari&go=1 logger deg inn via token-lenka, slik
panelet skal gjøre.

To ting i oppsettet som ser rart ut, men ikke er det:

- authorization_endpoint peker på localhost:9000 fordi nettleseren skal
  dit, mens token_endpoint peker på idp:9000 fordi announcer selv snakker
  med den inne i compose-nettet. Delt horisont. Det tester samtidig at
  manuelle endepunkter virker, som er veien når et panel mangler discovery.
- UV_PROJECT_ENVIRONMENT=/opt/venv i bildet: /app er et bind-mount av
  repoet, og uten den ville containerens uv sync overskrevet .venv som den
  kjørende tjenesten bruker.

/run/user/1000 er montert inn, så pw-play i containeren spiller på vertens
anlegg. Det er verifisert; kommenter ut linja hvis du bare skal teste UI.

Docker er bare for testing. På en ekte maskin kjører appen nativt med
install.sh, fordi lyden skal ut på maskinens egen lydutgang.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 15:44:19 +02:00