Flere anlegg – hovedscene, resepsjon, crew – og ett sted å sende fra, med avkryssing for hvilke soner meldingen skal til. Sentralen er en vanlig announcer med [zones] i config. En sone er en vanlig announcer med ANNOUNCER_MESH_SECRET. Samme image, ingen mode, og en sone kan brukes alene hvis sentralen faller ut. Hver avhengighet med C-kode har aarch64-wheel i uv.lock, så det samme bildet går på en Raspberry Pi uten å kompilere noe. Sentralen syntetiserer én gang og sender ferdig WAV (POST /api/play, multipart). Sonen trenger da verken nett, edge-tts eller Pipers 670 MB for det den får – Item.wav er hele endringen i avspillingen. Køene forblir per sone: dispatcheren har egen tråd og egen kø, leverer til hver sone i hver sin tråd med timeout, og går videre. En opptatt hovedscene holder ikke igjen resepsjonen, og en sone som er nede feiler alene – synlig per sone i UI og revisjonslogg. Auth gjenbrukes: ny leverandør mesh på header X-Mesh-Key (ikke Bearer – den leses av JWT-leverandøren, som avviser alt som ikke er et JWT før neste får prøve), med én handling, play. To regler som fulgte med: - play følger aldri med «alt åpent». Uten det kunne hvem som helst POSTe en WAV til en åpen sone og få den spilt. Testen for åpen modus fanget det. - En legitimasjon som vises fram og er feil gir 401 også der gjester ellers har lov. Før slapp en feil PIN i åpen modus stille gjennom som gjest. mesh teller ikke som innlogging for folk: en sone med bare nøkkelen har fortsatt åpen side lokalt. Planlagte sendinger husker sonene sine og fordeles gjennom dispatcheren; planen ender som sent hvis minst én sone tok imot, med «hovedscene: kø #3, crew: nede» som note. Hodeløs Pi: install.sh kjører loginctl enable-linger, ellers finnes ikke lydsocketen når ingen logger inn. docs/mesh.md tar også raspi-config for jack framfor HDMI. 38 nye tester, 148 totalt. Fordelingen testes uten nett med stubbet transport; selve HTTP-en testes mot en levende werkzeug-server, så den håndrullede multiparten møter Flask sin parser. Verifisert i compose med sentral og to soner: til alle, bare til én, og én sone stoppet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
157 lines
5.8 KiB
TOML
157 lines
5.8 KiB
TOML
# Kopier til config.toml og endre det du vil ha annerledes. Alt er valgfritt:
|
||
# uten fila kjører appen med standardverdiene nedenfor, uten innlogging.
|
||
#
|
||
# cp config.example.toml config.toml
|
||
#
|
||
# Hver nøkkel kan overstyres av en miljøvariabel, se tabellen i AGENTS.md.
|
||
# Hemmeligheter bør settes slik istedenfor å stå her, f.eks. i systemd-enheten:
|
||
# Environment=ANNOUNCER_JWT_SECRET=...
|
||
# config.toml er i .gitignore.
|
||
|
||
[app]
|
||
title = "Announcer"
|
||
subtitle = "Skriv meldingen, forhåndslytt, og send den ut på anlegget."
|
||
accent = "#4fd1c5"
|
||
idle_text = "Stille · ingenting i kø"
|
||
message_placeholder = "Skriv meldingen som skal leses opp …"
|
||
max_chars = 600
|
||
default_voice = "finn"
|
||
default_chime = "dingdong"
|
||
|
||
# Knapper over tekstfeltet. Tom liste = ingen knapper.
|
||
quick_phrases = [
|
||
"Pizzaen er kommet. Hent i kantina!",
|
||
"Turneringen starter om ti minutter.",
|
||
"Husk å rydde rundt plassen din. Takk!",
|
||
]
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Mesh: én sentral som fordeler til flere soner
|
||
# --------------------------------------------------------------------------
|
||
# Settes BARE på sentralen. Navn -> "local" (eget anlegg) eller URL til en
|
||
# sone. Sonene er vanlige announcere med ANNOUNCER_MESH_SECRET satt i .env –
|
||
# samme hemmelighet som sentralen. En sone kan brukes alene også.
|
||
# Sentralen syntetiserer én gang og sender ferdig WAV; sonen trenger
|
||
# verken nett eller stemmer for det den får derfra.
|
||
#
|
||
# [zones]
|
||
# her = "local"
|
||
# hovedscene = "http://10.20.70.11:8090"
|
||
# resepsjon = "http://10.20.70.12:8090"
|
||
# crew = "http://10.20.70.13:8090"
|
||
|
||
[mesh]
|
||
# secret settes som ANNOUNCER_MESH_SECRET i .env, ikke her
|
||
timeout_seconds = 5
|
||
poll_seconds = 2
|
||
|
||
[server]
|
||
host = "0.0.0.0"
|
||
port = 8090
|
||
# Slå på bare når appen står bak HTTPS. Setter Secure på sesjonscookien; på
|
||
# rå HTTP ville ingen klart å logge inn med den på.
|
||
behind_tls = false
|
||
# Trengs bare bak en omvendt proxy, for at OIDC-redirect skal peke riktig.
|
||
# public_url = "https://announcer.example.no"
|
||
# Tom = nøkkel genereres én gang og lagres i instance/secret_key.
|
||
# secret_key = ""
|
||
|
||
[paths]
|
||
# Kommenter inn hvis noe skal ligge et annet sted enn ved siden av koden.
|
||
# voices = "voices"
|
||
# cache = "cache"
|
||
# chimes = "chimes"
|
||
# log = "logs/announcements.jsonl"
|
||
# state = "instance"
|
||
|
||
[schedule]
|
||
# Slingringsmonn etter planlagt tid. Tråden våkner med noen sekunders
|
||
# mellomrom, og en kjapp omstart skal ikke miste en sending. Mer enn dette
|
||
# for sent regnes som gått glipp av, og sendes *ikke* – et utrop om noe som
|
||
# skjedde for en time siden er feil å spille.
|
||
grace_seconds = 120
|
||
# Fanger opp tastefeil i årstall.
|
||
max_days_ahead = 30
|
||
tick_seconds = 20
|
||
|
||
[audio]
|
||
# pw-play --target / paplay --device. Tom = systemets standardutgang.
|
||
# Finn navnet med: pactl list short sinks
|
||
sink = ""
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Innlogging
|
||
# --------------------------------------------------------------------------
|
||
# Tom liste her og ingen av blokkene under utfylt = alt er åpent. Det er
|
||
# standarden, og helt greit på et lukket nett.
|
||
#
|
||
# En metode er aktiv når den står i providers *eller* når den er ferdig
|
||
# konfigurert nedenfor. Derfor holder det å sette en PIN for å slå på PIN.
|
||
# Rekkefølgen her bestemmer hvilken som prøves først.
|
||
|
||
[auth]
|
||
providers = []
|
||
# providers = ["oidc", "jwt", "pin"]
|
||
|
||
# --- PIN: én delt kode. Virker uten nett, så den er verdt å ha i reserve. ---
|
||
[auth.pin]
|
||
pin = ""
|
||
role = "admin"
|
||
label = "PIN"
|
||
hint = "Spør vaktleder"
|
||
|
||
# --- JWT: crew-panelet signerer et token. Se docs/crew-integration.md. -----
|
||
[auth.jwt]
|
||
issuer = "https://crew.example.no"
|
||
audience = "announcer"
|
||
# Ett av disse to. Delt hemmelighet er enklest; JWKS er bedre fordi
|
||
# announcer da aldri ser en nøkkel som kan lage tokens.
|
||
# secret = "sett-via-ANNOUNCER_JWT_SECRET"
|
||
# jwks_url = "https://crew.example.no/.well-known/jwks.json"
|
||
# Tom = HS256 med secret, RS256/ES256 med jwks_url.
|
||
algorithms = []
|
||
# Hvor rollene står i tokenet. Punktum for nestet, f.eks. "realm_access.roles".
|
||
role_claim = "roles"
|
||
# Hvilken rolle alle med gyldig token får hvis tokenet ikke sier noe.
|
||
default_role = ""
|
||
# Hvor lenge sesjonen fra /auth/token varer, hvis tokenet ikke har kortere exp.
|
||
session_seconds = 43200
|
||
# Slingringsmonn på exp/nbf i sekunder, for klokker som ikke står helt likt.
|
||
# En maskin på et arrangement har ikke alltid NTP i orden.
|
||
leeway = 30
|
||
|
||
# --- OIDC: vanlig innlogging i nettleseren, autorisasjonskode med PKCE. ----
|
||
[auth.oidc]
|
||
issuer = "https://crew.example.no"
|
||
client_id = ""
|
||
# client_secret = "sett-via-ANNOUNCER_OIDC_CLIENT_SECRET"
|
||
scopes = ["openid", "profile", "email"]
|
||
role_claim = "roles"
|
||
default_role = ""
|
||
label = "Logg inn med crew-kontoen"
|
||
# Bare hvis panelet mangler .well-known/openid-configuration:
|
||
# authorization_endpoint = "https://crew.example.no/oauth/authorize"
|
||
# token_endpoint = "https://crew.example.no/oauth/token"
|
||
# jwks_url = "https://crew.example.no/.well-known/jwks.json"
|
||
# end_session_endpoint = "https://crew.example.no/logout"
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Roller
|
||
# --------------------------------------------------------------------------
|
||
# Rollenavnene kommer fra tokenet (role_claim). Handlinger:
|
||
# view se køen og hva som spilles nå
|
||
# preview forhåndslytte (synteser lyd)
|
||
# announce sende ut på anlegget
|
||
# schedule planlegge en sending fram i tid, og avbryte sine egne
|
||
# play ta imot ferdig lyd fra sentralen (mesh). Bare rollen «mesh».
|
||
# stop stoppe det som spilles
|
||
# clear tømme køen
|
||
# "*" betyr alt.
|
||
|
||
[roles]
|
||
# Alle som ikke er innlogget. ["view"] er nyttig for en statusskjerm på veggen.
|
||
anonymous = []
|
||
crew = ["view", "preview", "announce", "schedule"]
|
||
admin = ["*"]
|
||
# Sentralen, når den snakker med denne maskina som sone. Ikke endre.
|
||
mesh = ["play", "view"]
|