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>
This commit is contained in:
parent
6aaa98d8b1
commit
6c1253f54c
7 changed files with 372 additions and 177 deletions
34
.env.example
Normal file
34
.env.example
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Kopier til .env. install.sh lager den for deg med riktige verdier.
|
||||||
|
#
|
||||||
|
# cp .env.example .env
|
||||||
|
|
||||||
|
# Bruker-ID som eier lydsocketen på verten. Stemmer ikke denne, kommer det
|
||||||
|
# ingen lyd. Finn den med: id -u ; id -g
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
XDG_RUNTIME_DIR=/run/user/1000
|
||||||
|
|
||||||
|
# Hvilken adresse sida skal svare på. Dette er det som avgrenser tilgang:
|
||||||
|
# Docker publiserer forbi ufw, så en ufw-regel hjelper ikke her.
|
||||||
|
# 0.0.0.0 alle nett maskina er på
|
||||||
|
# 100.75.x.x bare NetBird
|
||||||
|
# 127.0.0.1 bare maskina selv
|
||||||
|
BIND_IP=0.0.0.0
|
||||||
|
ANNOUNCER_PORT=8090
|
||||||
|
|
||||||
|
# Lydutgang. Tom = systemets standard. Finn navn med: pactl list short sinks
|
||||||
|
ANNOUNCER_SINK=
|
||||||
|
|
||||||
|
# Overstyrer tittelen uten en config.toml.
|
||||||
|
ANNOUNCER_TITLE=
|
||||||
|
|
||||||
|
# Full URL utenfra. Trengs bare for OIDC bak en omvendt proxy.
|
||||||
|
ANNOUNCER_PUBLIC_URL=
|
||||||
|
|
||||||
|
# --- hemmeligheter -------------------------------------------------------
|
||||||
|
# Tom = av. Se config.example.toml og docs/crew-integration.md.
|
||||||
|
ANNOUNCER_PIN=
|
||||||
|
ANNOUNCER_JWT_SECRET=
|
||||||
|
ANNOUNCER_OIDC_CLIENT_SECRET=
|
||||||
|
# Tom = genereres og lagres i data/instance/secret_key.
|
||||||
|
ANNOUNCER_SECRET_KEY=
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -9,3 +9,5 @@ instance/
|
||||||
.docker-data/
|
.docker-data/
|
||||||
# onnxruntime slenger fra seg denne når telemetri-ID ikke kan lagres
|
# onnxruntime slenger fra seg denne når telemetri-ID ikke kan lagres
|
||||||
:memory:.ses
|
:memory:.ses
|
||||||
|
data/
|
||||||
|
.env
|
||||||
|
|
|
||||||
122
AGENTS.md
122
AGENTS.md
|
|
@ -22,45 +22,98 @@ Filer:
|
||||||
| `config.example.toml` | kommentert mal; kopier til `config.toml` |
|
| `config.example.toml` | kommentert mal; kopier til `config.toml` |
|
||||||
| `docs/crew-integration.md` | kontrakten mot crew-panelet |
|
| `docs/crew-integration.md` | kontrakten mot crew-panelet |
|
||||||
| `tests/test_auth.py` | `uv run pytest` |
|
| `tests/test_auth.py` | `uv run pytest` |
|
||||||
| `compose.yaml` + `Dockerfile` | testoppsett, se under. Ikke produksjon |
|
| `Dockerfile` + `compose.yaml` | **drift** – appen kjøres i Docker |
|
||||||
|
| `.env.example` | bruker-ID, portbinding og hemmeligheter |
|
||||||
|
| `compose.test.yaml` | testoppsett med dummy-IdP, se under |
|
||||||
| `docker/dummy_idp.py` | falsk crew-panel, så OIDC kan prøves |
|
| `docker/dummy_idp.py` | falsk crew-panel, så OIDC kan prøves |
|
||||||
|
|
||||||
## Kjøring
|
## Kjøring
|
||||||
|
|
||||||
Tjenesten er en systemd-brukerenhet: `~/.config/systemd/user/announcer.service`
|
**Announcer kjører i Docker, på en egen klient som er koblet til
|
||||||
(enabled, port 8090). `systemctl --user restart announcer` etter endringer i
|
høyttaleranlegget – aldri på kitsune.** Kitsune er utviklingsmaskin; der er
|
||||||
koden. Manuelt: `uv run announcer.py --port 8090`.
|
det bare koden som bor.
|
||||||
|
|
||||||
Ny maskin (Debian/Ubuntu/Mint): `git clone https://git.1x1.no/kent/announcer.git`
|
Ny klient (Debian/Ubuntu/Mint):
|
||||||
og `./install.sh` gjør alt (apt, uv, Python 3.13, stemmer, enhet, ufw). Finnes
|
|
||||||
NetBird (`wt0`) skrur det på ufw med deny incoming og åpner 22, 22022 og porten
|
|
||||||
kun på `wt0`. Kan kjøres på nytt, og rydder da bort en gammel
|
|
||||||
`sg-announcer.service` hvis den ligger der. Avspilling bruker `pw-play` når
|
|
||||||
PipeWire kjører, ellers `paplay` (`player_cmd()`), så Mint 21 med PulseAudio
|
|
||||||
virker også.
|
|
||||||
|
|
||||||
### Omdøpingen: ett punkt gjenstår
|
```bash
|
||||||
|
git clone https://git.1x1.no/kent/announcer.git
|
||||||
|
cd announcer && ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
Mappa er flyttet til `~/Work/announcer`, enheten heter `announcer.service`, og
|
`install.sh` installerer Docker om den mangler, lager `.env` med riktig
|
||||||
remote peker på `https://git.1x1.no/kent/announcer.git`. **Men repoet i Forgejo
|
bruker-ID og portbinding, oppretter `data/`, bygger bildet, starter
|
||||||
heter `annuoncer`** – en skrivefeil. Rett navnet i Forgejo, så virker remote;
|
containeren og setter ufw. Den rydder også bort en eventuell gammel nativ
|
||||||
Forgejo legger selv inn en omdirigering fra det gamle navnet.
|
`announcer.service`/`sg-announcer.service`. Kan kjøres på nytt; en `.env` som
|
||||||
|
finnes blir ikke overskrevet.
|
||||||
|
|
||||||
Flytter du mappa igjen: `.venv` har absolutte stier i `pyvenv.cfg` og i
|
Drift:
|
||||||
shebangen til hvert konsollskript, så `uv sync` alene er ikke nok. Da må
|
|
||||||
`rm -rf .venv && uv sync` til, og tjenesten startes om.
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f # følg loggen
|
||||||
|
docker compose restart # etter endringer i data/config.toml
|
||||||
|
docker compose up -d --build # etter endringer i koden
|
||||||
|
cat data/logs/announcements.jsonl # hvem som sendte hva
|
||||||
|
```
|
||||||
|
|
||||||
|
Alt som skal overleve en ny container ligger i `data/` på verten
|
||||||
|
(bind-mount, ikke navngitt volum, så du kan lese loggen og slippe egne
|
||||||
|
jingler i `data/chimes/` uten å gå inn i containeren):
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| `data/voices/` | Piper-modellene, ~670 MB, lastes ned ved første oppstart |
|
||||||
|
| `data/cache/` | ferdige WAV-er etter innholdshash. Trygt å slette |
|
||||||
|
| `data/chimes/` | egne jinglefiler |
|
||||||
|
| `data/logs/` | revisjonsloggen |
|
||||||
|
| `data/instance/` | sesjonsnøkkelen |
|
||||||
|
| `data/config.toml` | valgfri. Uten den er sida åpen |
|
||||||
|
|
||||||
|
### Lyden ut av containeren
|
||||||
|
|
||||||
|
Det eneste virkelig uvanlige i oppsettet. Tre ting må stemme:
|
||||||
|
|
||||||
|
1. Hele vertens `XDG_RUNTIME_DIR` monteres inn på samme sti. Ikke bare
|
||||||
|
`pipewire-0`: PulseAudio bruker `pulse/native` pluss en cookie i samme
|
||||||
|
mappe, og ved å ta hele mappa virker begge lydservere uten at compose må
|
||||||
|
vite hvilken maskina har. `player_cmd()` velger `pw-play` eller `paplay`.
|
||||||
|
2. `user: "${PUID}:${PGID}"` må være brukeren som **eier** socketen på
|
||||||
|
verten. Stemmer ikke ID-en, kommer det ingen lyd. `install.sh` fyller inn
|
||||||
|
`id -u`/`id -g`.
|
||||||
|
3. Maskina må være logget inn grafisk, ellers finnes socketen ikke.
|
||||||
|
Containeren starter ved boot, altså før innlogging, og avspilling feiler
|
||||||
|
til noen logger inn. Det retter seg selv: `player_cmd()` slås opp per
|
||||||
|
melding, så første utrop etter innlogging virker uten omstart. Derfor
|
||||||
|
anbefaler `install.sh` automatisk innlogging.
|
||||||
|
|
||||||
|
### Brannmur: BIND_IP, ikke ufw
|
||||||
|
|
||||||
|
**Dockers publiserte porter går forbi ufw.** De settes opp i nat-tabellen,
|
||||||
|
før ufws regler, så `ufw deny` på porten gjør ingenting. Tilgangen styres
|
||||||
|
derfor av `BIND_IP` i `.env`:
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| `0.0.0.0` | alle nett maskina er på |
|
||||||
|
| `100.75.x.x` | bare NetBird. `install.sh` velger dette når `wt0` finnes |
|
||||||
|
| `127.0.0.1` | bare maskina selv |
|
||||||
|
|
||||||
|
ufw settes fortsatt for maskinens øvrige porter (ssh på `wt0`), men den er
|
||||||
|
ikke involvert i tilgangen til announcer. Dette er en endring fra den native
|
||||||
|
tida, da porten måtte åpnes eksplisitt i ufw.
|
||||||
|
|
||||||
## Testing i Docker
|
## Testing i Docker
|
||||||
|
|
||||||
`docker compose up --build` gir to containere:
|
`docker compose -f compose.test.yaml up --build` gir to containere:
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `http://localhost:8091` | announcer med PIN, JWT **og** OIDC slått på (`docker/config.test.toml`) |
|
| `http://localhost:8091` | announcer med PIN, JWT **og** OIDC slått på (`docker/config.test.toml`) |
|
||||||
| `http://localhost:9000` | `docker/dummy_idp.py`, som later som den er crew-panelet |
|
| `http://localhost:9000` | `docker/dummy_idp.py`, som later som den er crew-panelet |
|
||||||
|
|
||||||
Port 8091 med vilje, så den ikke kolliderer med tjenesten på 8090.
|
Egen fil, eget prosjektnavn og port 8091, så den ikke kan forveksles med
|
||||||
|
eller forstyrre den som kjører i drift. Begge porter er bundet til
|
||||||
|
`127.0.0.1`, så testriggen – som har PIN `1234` – aldri ligger åpen på
|
||||||
|
LAN-et.
|
||||||
|
|
||||||
Dummy-IdP-en finnes fordi crew-panelet ikke har OIDC ennå: uten den kan
|
Dummy-IdP-en finnes fordi crew-panelet ikke har OIDC ennå: uten den kan
|
||||||
OIDC-veien ikke prøves i det hele tatt. Den kjører en ekte
|
OIDC-veien ikke prøves i det hele tatt. Den kjører en ekte
|
||||||
|
|
@ -80,9 +133,11 @@ Lyd: `/run/user/1000` er montert inn, så `pw-play` i containeren spiller på
|
||||||
vertens anlegg. Kommenter ut den linja i `compose.yaml` hvis du bare skal
|
vertens anlegg. Kommenter ut den linja i `compose.yaml` hvis du bare skal
|
||||||
teste innlogging.
|
teste innlogging.
|
||||||
|
|
||||||
`UV_PROJECT_ENVIRONMENT=/opt/venv` i bildet er ikke pynt: `/app` er et
|
`compose.test.yaml` bind-monterer koden, så endringer slår inn med en
|
||||||
bind-mount av repoet, og uten det ville containerens `uv sync` overskrevet
|
restart. Driftsbildet kopierer koden inn i stedet, så det som kjører er
|
||||||
`.venv` som den kjørende tjenesten bruker.
|
uavhengig av hva som ligger i mappa. `UV_PROJECT_ENVIRONMENT=/opt/venv` er
|
||||||
|
derfor ikke pynt: med bind-mount ville containerens `uv sync` ellers
|
||||||
|
overskrevet `.venv` på verten.
|
||||||
|
|
||||||
## Konfigurasjon
|
## Konfigurasjon
|
||||||
|
|
||||||
|
|
@ -138,6 +193,14 @@ Uten den logges alle ut ved hver `systemctl restart`. `SESSION_COOKIE_SECURE`
|
||||||
settes bare når `server.behind_tls` er sann – appen kjører på rå HTTP over LAN
|
settes bare når `server.behind_tls` er sann – appen kjører på rå HTTP over LAN
|
||||||
og NetBird, og med Secure på ville cookien aldri blitt sendt.
|
og NetBird, og med Secure på ville cookien aldri blitt sendt.
|
||||||
|
|
||||||
|
## Utvikling på kitsune
|
||||||
|
|
||||||
|
`uv run announcer.py --port 8099` kjører appen direkte, uten Docker. Det er
|
||||||
|
veien for å prøve en endring kjapt; `uv run pytest` for testene. Men
|
||||||
|
**ingen tjeneste skal stå og kjøre på kitsune** – det var slik det var før, og
|
||||||
|
det er ryddet bort. Skal noe prøves med innlogging, bruk
|
||||||
|
`compose.test.yaml`.
|
||||||
|
|
||||||
## Hvordan det henger sammen
|
## Hvordan det henger sammen
|
||||||
|
|
||||||
- `/api/config` er alt sida trenger for å tegne seg selv: branding, stemmer,
|
- `/api/config` er alt sida trenger for å tegne seg selv: branding, stemmer,
|
||||||
|
|
@ -187,10 +250,15 @@ Vil Kent ha enda bedre lyd: ElevenLabs (nøkkel, best norsk) eller OpenAI
|
||||||
|
|
||||||
## Feller
|
## Feller
|
||||||
|
|
||||||
- **ufw**: appen kjører nativt, så Docker-smutthullet gjelder ikke. Porten må
|
- **ufw styrer ikke Docker-porter.** Se «Brannmur» over: tilgangen avgrenses
|
||||||
åpnes eksplisitt for LAN-et (`sudo ufw allow from 10.20.70.0/23 to any port 8090 proto tcp`).
|
med `BIND_IP` i `.env`. En `ufw deny` på porten gir falsk trygghet.
|
||||||
|
- **Ingen lyd?** Nesten alltid `PUID`/`PGID` i `.env` som ikke er brukeren
|
||||||
|
som eier lydsocketen, eller at maskina ikke er logget inn grafisk. Sjekk
|
||||||
|
hva containeren velger:
|
||||||
|
`docker compose exec announcer python -c "import announcer,pathlib;print(announcer.player_cmd(pathlib.Path('/tmp/x.wav')))"`
|
||||||
- `pkill -f announcer.py` fra et skall som selv har `announcer.py` i
|
- `pkill -f announcer.py` fra et skall som selv har `announcer.py` i
|
||||||
kommandolinja dreper skallet. Bruk `systemctl --user`.
|
kommandolinja dreper skallet. Gjelder utvikling med `uv run`; i drift er
|
||||||
|
det `docker compose`.
|
||||||
- `player = Player()` står på modulnivå, så avspillingstråden starter ved
|
- `player = Player()` står på modulnivå, så avspillingstråden starter ved
|
||||||
`import announcer` – også i `install.sh`, som importerer modulen for å laste
|
`import announcer` – også i `install.sh`, som importerer modulen for å laste
|
||||||
ned stemmer. Harmløst, men greit å vite.
|
ned stemmer. Harmløst, men greit å vite.
|
||||||
|
|
|
||||||
38
Dockerfile
38
Dockerfile
|
|
@ -1,23 +1,20 @@
|
||||||
# Testbilde for announcer. Ikke ment for produksjon – på en ekte maskin
|
# Announcer kjøres i Docker på klienten som er koblet til høyttaleranlegget.
|
||||||
# kjører appen nativt med install.sh, fordi lyden skal ut på maskinens egen
|
|
||||||
# lydutgang og det er vondt å få til gjennom et containerlag.
|
|
||||||
#
|
#
|
||||||
# Her er poenget å kunne prøve innlogging (PIN, JWT, OIDC) uten å røre
|
# Lyden er det som gjør bildet litt uvanlig: containeren må nå vertens
|
||||||
# tjenesten som kjører, og uten sudo eller brannmurendringer.
|
# lydserver. Det løses i compose.yaml ved å montere inn vertens
|
||||||
|
# XDG_RUNTIME_DIR og kjøre som samme bruker-ID som eier socketen – ikke her.
|
||||||
FROM python:3.13-slim
|
FROM python:3.13-slim
|
||||||
|
|
||||||
# ffmpeg trengs av edge_pcm() og av egne jinglefiler. pw-play/paplay er bare
|
# ffmpeg: edge_pcm() dekoder MP3 med den, og egne jinglefiler normaliseres
|
||||||
# nyttig hvis vertens PipeWire-socket monteres inn; uten den feiler selve
|
# med den. pipewire-bin gir pw-play, pulseaudio-utils gir paplay; koden
|
||||||
# avspillingen, men resten av appen virker.
|
# velger selv hvilken som finnes (player_cmd()), så begge lydservere virker.
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends ffmpeg ca-certificates curl \
|
&& apt-get install -y --no-install-recommends \
|
||||||
&& (apt-get install -y --no-install-recommends pipewire-bin pulseaudio-utils || true) \
|
ffmpeg ca-certificates pipewire-bin pulseaudio-utils \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||||
|
|
||||||
# Venv utenfor /app: /app er et bind-mount av repoet, og en .venv bygget her
|
|
||||||
# ville ellers overskrevet den verten bruker til den kjørende tjenesten.
|
|
||||||
ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
|
ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
|
||||||
UV_COMPILE_BYTECODE=1 \
|
UV_COMPILE_BYTECODE=1 \
|
||||||
PATH=/opt/venv/bin:$PATH \
|
PATH=/opt/venv/bin:$PATH \
|
||||||
|
|
@ -25,7 +22,20 @@ ENV UV_PROJECT_ENVIRONMENT=/opt/venv \
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY pyproject.toml uv.lock ./
|
COPY pyproject.toml uv.lock ./
|
||||||
RUN uv sync --frozen
|
RUN uv sync --frozen --no-dev
|
||||||
|
|
||||||
|
# Koden sist, så avhengighetslaget gjenbrukes når bare appen endres.
|
||||||
|
COPY announcer.py auth.py config.py index.html ./
|
||||||
|
|
||||||
|
# Alt som skal overleve en ny container ligger under /data, som monteres
|
||||||
|
# inn fra verten. Stemmene er ~670 MB og lastes ned ved første oppstart.
|
||||||
|
ENV ANNOUNCER_VOICES=/data/voices \
|
||||||
|
ANNOUNCER_CACHE=/data/cache \
|
||||||
|
ANNOUNCER_CHIMES=/data/chimes \
|
||||||
|
ANNOUNCER_LOG=/data/logs/announcements.jsonl \
|
||||||
|
ANNOUNCER_STATE=/data/instance \
|
||||||
|
ANNOUNCER_CONFIG=/data/config.toml \
|
||||||
|
ANNOUNCER_PORT=8090
|
||||||
|
|
||||||
EXPOSE 8090
|
EXPOSE 8090
|
||||||
CMD ["python", "announcer.py", "--host", "0.0.0.0", "--port", "8090"]
|
CMD ["python", "announcer.py", "--host", "0.0.0.0"]
|
||||||
|
|
|
||||||
47
compose.test.yaml
Normal file
47
compose.test.yaml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Testoppsett, ikke drift:
|
||||||
|
#
|
||||||
|
# docker compose -f compose.test.yaml up --build
|
||||||
|
#
|
||||||
|
# http://localhost:8091 announcer med PIN, JWT og OIDC slått på
|
||||||
|
# http://localhost:9000 dummy-IdP som står for crew-panelet
|
||||||
|
#
|
||||||
|
# Egen fil og eget prosjektnavn, så den ikke kan forveksles med eller
|
||||||
|
# forstyrre den som kjører i drift. Porten er 8091 av samme grunn.
|
||||||
|
# Koden bind-monteres her, så endringer slår inn med en restart.
|
||||||
|
name: announcer-test
|
||||||
|
|
||||||
|
services:
|
||||||
|
announcer:
|
||||||
|
build: .
|
||||||
|
ports: ["127.0.0.1:8091:8090"]
|
||||||
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
|
environment:
|
||||||
|
ANNOUNCER_CONFIG: /app/docker/config.test.toml
|
||||||
|
ANNOUNCER_CACHE: /data/cache
|
||||||
|
ANNOUNCER_LOG: /data/logs/announcements.jsonl
|
||||||
|
ANNOUNCER_STATE: /data/instance
|
||||||
|
ANNOUNCER_VOICES: /data/voices
|
||||||
|
ANNOUNCER_CHIMES: /data/chimes
|
||||||
|
ANNOUNCER_JWT_SECRET: test-hemmelighet-kun-for-docker-ikke-bruk-ekte
|
||||||
|
ANNOUNCER_OIDC_CLIENT_SECRET: test-client-secret-kun-for-docker
|
||||||
|
ANNOUNCER_PUBLIC_URL: http://localhost:8091
|
||||||
|
XDG_RUNTIME_DIR: ${XDG_RUNTIME_DIR:-/run/user/1000}
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- ./data:/data
|
||||||
|
- ${XDG_RUNTIME_DIR:-/run/user/1000}:${XDG_RUNTIME_DIR:-/run/user/1000}
|
||||||
|
depends_on: [idp]
|
||||||
|
|
||||||
|
idp:
|
||||||
|
build: .
|
||||||
|
ports: ["127.0.0.1:9000:9000"]
|
||||||
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
|
environment:
|
||||||
|
IDP_ISSUER: http://localhost:9000
|
||||||
|
IDP_CLIENT_ID: announcer
|
||||||
|
IDP_CLIENT_SECRET: test-client-secret-kun-for-docker
|
||||||
|
ANNOUNCER_JWT_SECRET: test-hemmelighet-kun-for-docker-ikke-bruk-ekte
|
||||||
|
ANNOUNCER_PUBLIC_URL: http://localhost:8091
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
command: ["python", "docker/dummy_idp.py"]
|
||||||
80
compose.yaml
80
compose.yaml
|
|
@ -1,45 +1,53 @@
|
||||||
# Testoppsett, ikke produksjon: docker compose up --build
|
# Announcer i drift på klienten ved anlegget.
|
||||||
#
|
#
|
||||||
# http://localhost:8091 announcer (PIN + JWT + OIDC slått på)
|
# ./install.sh setter opp alt, inkludert .env
|
||||||
# http://localhost:9000 dummy-IdP som later som den er crew-panelet
|
# docker compose up -d senere, for hånd
|
||||||
#
|
#
|
||||||
# Porten er 8091 med vilje, så den ikke kolliderer med tjenesten på 8090.
|
# Testoppsettet med dummy-IdP ligger i compose.test.yaml, ikke her.
|
||||||
name: announcer-test
|
name: announcer
|
||||||
|
|
||||||
services:
|
services:
|
||||||
announcer:
|
announcer:
|
||||||
build: .
|
build: .
|
||||||
ports: ["8091:8090"]
|
image: announcer:local
|
||||||
user: "1000:1000"
|
restart: unless-stopped
|
||||||
environment:
|
|
||||||
ANNOUNCER_CONFIG: /app/docker/config.test.toml
|
# BIND_IP er sikkerhetsmekanismen her, ikke ufw. Dockers publiserte
|
||||||
# Buffer, logg og sesjonsnøkkel holdes utenfor repoet, så testing ikke
|
# porter går forbi ufw, så en ufw-regel ville ikke stengt noen ute.
|
||||||
# blander seg med det den kjørende tjenesten eier.
|
# install.sh setter den til NetBird-adressen når wt0 finnes.
|
||||||
ANNOUNCER_CACHE: /data/cache
|
ports:
|
||||||
ANNOUNCER_LOG: /data/logs/announcements.jsonl
|
- "${BIND_IP:-0.0.0.0}:${ANNOUNCER_PORT:-8090}:8090"
|
||||||
ANNOUNCER_STATE: /data/instance
|
|
||||||
ANNOUNCER_VOICES: /app/voices
|
# Må være brukeren som eier lydsocketen på verten, ellers kommer det
|
||||||
ANNOUNCER_JWT_SECRET: test-hemmelighet-kun-for-docker-ikke-bruk-ekte
|
# ingen lyd. install.sh fyller inn id -u / id -g.
|
||||||
ANNOUNCER_OIDC_CLIENT_SECRET: test-client-secret-kun-for-docker
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
# Må peke dit nettleseren ser appen, ellers blir OIDC-redirecten feil.
|
|
||||||
ANNOUNCER_PUBLIC_URL: http://localhost:8091
|
|
||||||
XDG_RUNTIME_DIR: /run/user/1000
|
|
||||||
volumes:
|
|
||||||
- .:/app
|
|
||||||
- ./.docker-data:/data
|
|
||||||
# Lyd: gir containeren vertens PipeWire. Kommenter ut hvis du bare
|
|
||||||
# skal teste innlogging og UI – da feiler avspilling, men ikke annet.
|
|
||||||
- /run/user/1000:/run/user/1000
|
|
||||||
depends_on: [idp]
|
|
||||||
|
|
||||||
idp:
|
|
||||||
build: .
|
|
||||||
ports: ["9000:9000"]
|
|
||||||
user: "1000:1000"
|
|
||||||
environment:
|
environment:
|
||||||
IDP_ISSUER: http://localhost:9000
|
ANNOUNCER_SINK: ${ANNOUNCER_SINK:-}
|
||||||
IDP_CLIENT_ID: announcer
|
ANNOUNCER_TITLE: ${ANNOUNCER_TITLE:-}
|
||||||
IDP_CLIENT_SECRET: test-client-secret-kun-for-docker
|
ANNOUNCER_PUBLIC_URL: ${ANNOUNCER_PUBLIC_URL:-}
|
||||||
|
# Hemmeligheter: hører i .env, ikke i config.toml.
|
||||||
|
ANNOUNCER_PIN: ${ANNOUNCER_PIN:-}
|
||||||
|
ANNOUNCER_JWT_SECRET: ${ANNOUNCER_JWT_SECRET:-}
|
||||||
|
ANNOUNCER_OIDC_CLIENT_SECRET: ${ANNOUNCER_OIDC_CLIENT_SECRET:-}
|
||||||
|
ANNOUNCER_SECRET_KEY: ${ANNOUNCER_SECRET_KEY:-}
|
||||||
|
XDG_RUNTIME_DIR: ${XDG_RUNTIME_DIR:-/run/user/1000}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
# Stemmer, buffer, logg, sesjonsnøkkel, egne jingler og config.toml.
|
||||||
command: ["python", "docker/dummy_idp.py"]
|
- ./data:/data
|
||||||
|
# Lyden. Hele XDG_RUNTIME_DIR, ikke bare én socket, så både
|
||||||
|
# PipeWire (pipewire-0) og PulseAudio (pulse/native + cookie) virker.
|
||||||
|
- ${XDG_RUNTIME_DIR:-/run/user/1000}:${XDG_RUNTIME_DIR:-/run/user/1000}
|
||||||
|
|
||||||
|
# Containeren starter ved boot, altså før noen er logget inn grafisk.
|
||||||
|
# Da finnes ikke lydsocketen ennå, og avspilling feiler. Det retter seg
|
||||||
|
# selv: player_cmd() slås opp per melding, så første utrop etter
|
||||||
|
# innlogging virker uten at noe må startes om.
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c",
|
||||||
|
"import urllib.request;urllib.request.urlopen('http://127.0.0.1:8090/api/config',timeout=5)"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
start_period: 180s # første oppstart laster ned ~670 MB stemmer
|
||||||
|
retries: 3
|
||||||
|
|
|
||||||
222
install.sh
222
install.sh
|
|
@ -1,15 +1,20 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Installerer Announcer på en Debian/Ubuntu-maskin (testet mot Linux Mint).
|
# Setter opp Announcer på klienten som er koblet til høyttaleranlegget.
|
||||||
|
# Debian/Ubuntu/Mint. Appen kjører i Docker; dette skriptet ordner Docker,
|
||||||
|
# .env, datamapper, brannmur og autostart.
|
||||||
#
|
#
|
||||||
# git clone https://git.1x1.no/kent/announcer.git
|
# git clone https://git.1x1.no/kent/announcer.git
|
||||||
# cd announcer && ./install.sh
|
# cd announcer && ./install.sh
|
||||||
#
|
#
|
||||||
# Kjør som vanlig bruker (ikke sudo); skriptet spør om passord der det trengs.
|
# Kjør som vanlig bruker (ikke sudo); skriptet spør om passord der det trengs.
|
||||||
# Valgfritt: ANNOUNCER_PIN=1234 ./install.sh -> sida krever PIN
|
# Valgfritt:
|
||||||
# ANNOUNCER_PORT=8090 ./install.sh -> annen port
|
# ANNOUNCER_PIN=1234 ./install.sh sida krever PIN
|
||||||
# ANNOUNCER_TITLE="Utrop" ./install.sh
|
# ANNOUNCER_PORT=8090 ./install.sh annen port
|
||||||
# Resten av konfigurasjonen hører hjemme i config.toml, se config.example.toml.
|
# ANNOUNCER_TITLE="Utrop" ./install.sh annen tittel
|
||||||
# Kan kjøres på nytt; da oppdateres avhengigheter og tjenesten startes om.
|
# BIND_IP=127.0.0.1 ./install.sh bare maskina selv
|
||||||
|
#
|
||||||
|
# Kan kjøres på nytt; da bygges bildet om og containeren startes om. En .env
|
||||||
|
# som finnes blir ikke overskrevet.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
|
@ -17,129 +22,150 @@ if [[ $EUID -eq 0 ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
APP="announcer"
|
|
||||||
OLD_APP="sg-announcer" # het dette før; enheten ryddes bort nedenfor
|
|
||||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PORT="${ANNOUNCER_PORT:-8090}"
|
cd "$REPO_DIR"
|
||||||
PIN="${ANNOUNCER_PIN:-}"
|
|
||||||
TITLE="${ANNOUNCER_TITLE:-}"
|
|
||||||
UNIT_DIR="$HOME/.config/systemd/user"
|
|
||||||
UNIT="$UNIT_DIR/$APP.service"
|
|
||||||
|
|
||||||
step() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
|
step() { printf '\n\033[1m==> %s\033[0m\n' "$*"; }
|
||||||
|
warn() { printf '\033[33m! %s\033[0m\n' "$*"; }
|
||||||
|
|
||||||
step "Systempakker (apt)"
|
# --------------------------------------------------------------------------
|
||||||
sudo apt-get update -qq
|
step "Rydder bort eventuell nativ installasjon"
|
||||||
sudo apt-get install -y -qq git curl ca-certificates ffmpeg pulseaudio-utils
|
# Tidligere versjoner kjørte appen direkte som en systemd-brukerenhet. To
|
||||||
# pw-play finnes i pipewire-bin på Mint 22 / Ubuntu 24.04. På eldre (PulseAudio)
|
# instanser ville slåss om porten, så den gamle skal bort.
|
||||||
# faller appen tilbake til paplay, så dette er ikke kritisk.
|
for old in sg-announcer announcer; do
|
||||||
sudo apt-get install -y -qq pipewire-bin 2>/dev/null || true
|
unit="$HOME/.config/systemd/user/$old.service"
|
||||||
|
if [[ -f "$unit" ]]; then
|
||||||
step "uv (Python-pakkehåndterer)"
|
systemctl --user disable --now "$old" >/dev/null 2>&1 || true
|
||||||
if command -v uv >/dev/null 2>&1; then
|
rm -f "$unit"
|
||||||
UV="$(command -v uv)"
|
echo "fjernet $old.service (nativ)"
|
||||||
elif [[ -x "$HOME/.local/bin/uv" ]]; then
|
|
||||||
UV="$HOME/.local/bin/uv"
|
|
||||||
else
|
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
||||||
UV="$HOME/.local/bin/uv"
|
|
||||||
fi
|
fi
|
||||||
echo "uv: $UV ($("$UV" --version))"
|
done
|
||||||
|
systemctl --user daemon-reload 2>/dev/null || true
|
||||||
|
|
||||||
step "Python 3.13 og avhengigheter"
|
# --------------------------------------------------------------------------
|
||||||
cd "$REPO_DIR"
|
step "Docker"
|
||||||
"$UV" sync
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
mkdir -p cache chimes voices logs instance
|
echo "installerer Docker …"
|
||||||
|
curl -fsSL https://get.docker.com | sudo sh
|
||||||
|
fi
|
||||||
|
if ! id -nG "$USER" | tr ' ' '\n' | grep -qx docker; then
|
||||||
|
sudo usermod -aG docker "$USER"
|
||||||
|
warn "La $USER i docker-gruppa. Du må logge ut og inn igjen,"
|
||||||
|
warn "og så kjøre ./install.sh på nytt. Stopper her."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
warn "Docker svarer ikke. Er tjenesten startet? sudo systemctl enable --now docker"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
docker --version
|
||||||
|
docker compose version | head -1
|
||||||
|
|
||||||
step "Laster ned Piper-stemmene (lokal reserve, ca. 600 MB, kun første gang)"
|
# --------------------------------------------------------------------------
|
||||||
"$UV" run python -c "import logging, announcer; logging.basicConfig(level=logging.INFO, format='%(message)s'); announcer.ensure_voices()"
|
step "Oppsett (.env)"
|
||||||
|
# BIND_IP er det som avgjør hvem som når sida. ufw gjør det ikke: Dockers
|
||||||
step "Rydder bort gammel tjeneste"
|
# publiserte porter settes opp i nat-tabellen og går forbi ufws regler.
|
||||||
# Tjenesten het sg-announcer før. Uten dette slåss to enheter om porten.
|
NB_IP="$(ip -4 addr show wt0 2>/dev/null | awk '/inet /{sub(/\/.*/,"",$2);print $2;exit}')"
|
||||||
if [[ -f "$UNIT_DIR/$OLD_APP.service" ]]; then
|
if [[ -n "${BIND_IP:-}" ]]; then
|
||||||
systemctl --user disable --now "$OLD_APP" >/dev/null 2>&1 || true
|
:
|
||||||
rm -f "$UNIT_DIR/$OLD_APP.service"
|
elif [[ -n "$NB_IP" ]]; then
|
||||||
echo "fjernet $OLD_APP.service"
|
BIND_IP="$NB_IP"
|
||||||
|
echo "NetBird funnet – binder til $BIND_IP, så bare VPN-et når sida."
|
||||||
else
|
else
|
||||||
echo "ingenting å rydde."
|
BIND_IP="0.0.0.0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
step "systemd-brukertjeneste"
|
if [[ -f .env ]]; then
|
||||||
mkdir -p "$UNIT_DIR"
|
echo ".env finnes allerede, lar den stå. Slett den for å lage en ny."
|
||||||
{
|
|
||||||
cat <<UNIT
|
|
||||||
[Unit]
|
|
||||||
Description=Announcer – TTS-utrop på høyttaleranlegget
|
|
||||||
After=pipewire.service pulseaudio.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=$REPO_DIR
|
|
||||||
ExecStart=$UV run announcer.py
|
|
||||||
Environment=ANNOUNCER_PORT=$PORT
|
|
||||||
UNIT
|
|
||||||
if [[ -n "$PIN" ]]; then
|
|
||||||
echo "Environment=ANNOUNCER_PIN=$PIN"
|
|
||||||
else
|
else
|
||||||
echo "# Environment=ANNOUNCER_PIN=1234"
|
cat > .env <<ENV
|
||||||
|
# Laget av install.sh $(date +%Y-%m-%d). Se .env.example for forklaringer.
|
||||||
|
PUID=$(id -u)
|
||||||
|
PGID=$(id -g)
|
||||||
|
XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
|
||||||
|
BIND_IP=$BIND_IP
|
||||||
|
ANNOUNCER_PORT=${ANNOUNCER_PORT:-8090}
|
||||||
|
ANNOUNCER_SINK=${ANNOUNCER_SINK:-}
|
||||||
|
ANNOUNCER_TITLE=${ANNOUNCER_TITLE:-}
|
||||||
|
ANNOUNCER_PUBLIC_URL=${ANNOUNCER_PUBLIC_URL:-}
|
||||||
|
ANNOUNCER_PIN=${ANNOUNCER_PIN:-}
|
||||||
|
ANNOUNCER_JWT_SECRET=${ANNOUNCER_JWT_SECRET:-}
|
||||||
|
ANNOUNCER_OIDC_CLIENT_SECRET=${ANNOUNCER_OIDC_CLIENT_SECRET:-}
|
||||||
|
ANNOUNCER_SECRET_KEY=
|
||||||
|
ENV
|
||||||
|
chmod 600 .env # PIN og nøkler står i klartekst
|
||||||
|
echo "skrev .env (PUID=$(id -u), BIND_IP=$BIND_IP)"
|
||||||
fi
|
fi
|
||||||
if [[ -n "$TITLE" ]]; then
|
|
||||||
echo "Environment=ANNOUNCER_TITLE=$TITLE"
|
step "Datamapper"
|
||||||
else
|
mkdir -p data/{voices,cache,chimes,logs,instance}
|
||||||
echo "# Environment=ANNOUNCER_TITLE=Utrop"
|
[[ -f data/chimes/README.txt ]] || cp -n chimes/README.txt data/chimes/ 2>/dev/null || true
|
||||||
|
echo "data/ – stemmer, buffer, egne jingler, logg og sesjonsnøkkel"
|
||||||
|
if [[ ! -f data/config.toml ]]; then
|
||||||
|
echo "ingen data/config.toml: sida er åpen for alle som når den."
|
||||||
|
echo " vil du ha innlogging: cp config.example.toml data/config.toml"
|
||||||
fi
|
fi
|
||||||
cat <<'UNIT'
|
|
||||||
# Environment=ANNOUNCER_SINK=alsa_output.xxx
|
|
||||||
# Hemmeligheter hører hjemme her og ikke i config.toml:
|
|
||||||
# Environment=ANNOUNCER_JWT_SECRET=...
|
|
||||||
# Environment=ANNOUNCER_OIDC_CLIENT_SECRET=...
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=3
|
|
||||||
|
|
||||||
[Install]
|
# --------------------------------------------------------------------------
|
||||||
WantedBy=default.target
|
step "Bygger og starter"
|
||||||
UNIT
|
docker compose up -d --build
|
||||||
} > "$UNIT"
|
|
||||||
chmod 600 "$UNIT" # PIN og nøkler står i klartekst her
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
systemctl --user enable --now "$APP"
|
|
||||||
systemctl --user restart "$APP"
|
|
||||||
|
|
||||||
|
step "Venter på at tjenesten svarer"
|
||||||
|
# Første oppstart laster ned Piper-stemmene (~670 MB) før den svarer.
|
||||||
|
PORT="$(grep -E '^ANNOUNCER_PORT=' .env | cut -d= -f2)"
|
||||||
|
PROBE_IP="$BIND_IP"; [[ "$PROBE_IP" == "0.0.0.0" ]] && PROBE_IP=127.0.0.1
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
if curl -fsS -o /dev/null "http://$PROBE_IP:$PORT/api/config" 2>/dev/null; then
|
||||||
|
echo "svarer etter ~$((i*5)) s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
[[ $i -eq 1 ]] && echo "laster ned stemmer, dette tar noen minutter første gang …"
|
||||||
|
[[ $i -eq 60 ]] && warn "svarte ikke innen 5 min. Se: docker compose logs -f"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
step "Brannmur"
|
step "Brannmur"
|
||||||
if ip link show wt0 >/dev/null 2>&1; then
|
# Merk at ufw *ikke* styrer tilgangen til announcer – det gjør BIND_IP i
|
||||||
# NetBird er installert: slipp inn kun via VPN-grensesnittet, blokker alt annet.
|
# .env. ufw settes her bare for maskinens øvrige porter.
|
||||||
|
if [[ -n "$NB_IP" ]]; then
|
||||||
sudo ufw default deny incoming
|
sudo ufw default deny incoming
|
||||||
sudo ufw default allow outgoing
|
sudo ufw default allow outgoing
|
||||||
sudo ufw allow in on wt0 to any port 22 proto tcp comment "ssh via netbird"
|
sudo ufw allow in on wt0 to any port 22 proto tcp comment "ssh via netbird"
|
||||||
sudo ufw allow in on wt0 to any port 22022 proto tcp comment "netbird ssh-server"
|
sudo ufw allow in on wt0 to any port 22022 proto tcp comment "netbird ssh-server"
|
||||||
sudo ufw allow in on wt0 to any port "$PORT" proto tcp comment "$APP via netbird"
|
|
||||||
sudo ufw delete allow "$PORT/tcp" >/dev/null 2>&1 || true
|
|
||||||
sudo ufw --force enable
|
sudo ufw --force enable
|
||||||
echo "ufw aktiv: port $PORT, 22 og 22022 er åpne kun på wt0 (NetBird)."
|
echo "ufw aktiv: ssh kun på wt0."
|
||||||
echo "Vil du også nå sida fra lokalt LAN uten VPN:"
|
echo "announcer er bundet til $BIND_IP:$PORT – det er NetBird-adressen,"
|
||||||
echo " sudo ufw allow from <lan-subnett> to any port $PORT proto tcp"
|
echo "så sida nås bare over VPN-et. ufw er ikke involvert i det."
|
||||||
elif sudo ufw status 2>/dev/null | grep -q "Status: active"; then
|
elif command -v ufw >/dev/null 2>&1 && sudo ufw status 2>/dev/null | grep -q "Status: active"; then
|
||||||
sudo ufw allow "$PORT/tcp" comment "$APP"
|
warn "ufw er aktiv, men styrer ikke Docker-porter."
|
||||||
|
warn "Vil du begrense tilgangen: sett BIND_IP i .env og kjør docker compose up -d."
|
||||||
else
|
else
|
||||||
echo "ufw er ikke aktiv, ingen regel nødvendig."
|
echo "ufw er ikke aktiv."
|
||||||
|
[[ "$BIND_IP" == "0.0.0.0" ]] && warn "Sida er åpen på alle nett maskina er på."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 2
|
# --------------------------------------------------------------------------
|
||||||
IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
IP="$(ip -4 addr show scope global 2>/dev/null | awk '/inet /{sub(/\/.*/,"",$2);print $2;exit}')"
|
||||||
step "Ferdig"
|
step "Ferdig"
|
||||||
systemctl --user --no-pager status "$APP" | head -5 || true
|
docker compose ps
|
||||||
cat <<MSG
|
cat <<MSG
|
||||||
|
|
||||||
Åpne http://${IP:-<denne-maskinens-ip>}:$PORT fra hva som helst på nettet.
|
Åpne http://${BIND_IP/0.0.0.0/${IP:-<denne-maskinens-ip>}}:$PORT
|
||||||
|
|
||||||
Uten config.toml er sida åpen for alle som når porten. Vil du ha innlogging:
|
Lyden spilles i din innloggede økt, så maskinen må være logget inn grafisk.
|
||||||
cp config.example.toml config.toml og les kommentarene der.
|
Containeren starter ved boot, altså før innlogging – da feiler avspilling,
|
||||||
Mot crew-panelet: docs/crew-integration.md
|
men den retter seg selv ved første utrop etter at du er logget inn.
|
||||||
|
|
||||||
Tips for en laptop på arrangementet:
|
Tips for en laptop på arrangementet:
|
||||||
- Lyden spilles i din innloggede økt, så maskinen må være logget inn.
|
- Slå på automatisk innlogging (Innstillinger -> Påloggingsvindu).
|
||||||
Slå på automatisk innlogging (Innstillinger -> Påloggingsvindu).
|
|
||||||
- Hindre at den sover med lokket igjen:
|
- Hindre at den sover med lokket igjen:
|
||||||
sudo sed -i 's/^#\?HandleLidSwitch=.*/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
|
sudo sed -i 's/^#\?HandleLidSwitch=.*/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
|
||||||
sudo systemctl restart systemd-logind
|
sudo systemctl restart systemd-logind
|
||||||
- Logg: journalctl --user -u $APP -f
|
|
||||||
|
Drift:
|
||||||
|
docker compose logs -f følg loggen
|
||||||
|
docker compose restart etter endringer i data/config.toml
|
||||||
|
docker compose up -d --build etter endringer i koden
|
||||||
|
cat data/logs/announcements.jsonl hvem som sendte hva
|
||||||
MSG
|
MSG
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue