refactor: astroklient → astroklient-demo

Nazwa `astroklient` zostaje zarezerwowana dla przyszłej wersji produkcyjnej
programu; obecna, demonstracyjna nazywa się od teraz `astroklient-demo`.

Zmiana obejmuje katalog usługi, nazwę pliku testów, obraz w rejestrze
(astrololo-astroklient-demo), job w CI, pętlę budowania obrazów, tytuł i nagłówek
strony, nazwy loggerów, realm logowania, pole `layer` w /health oraz wymagania
PRE-28/29 w xlsx.

DWIE PUŁAPKI PODMIANY, obie sprawdzone po fakcie:

Zdublowany przyrostek. `astroklient-demo` zawiera `astroklient`, więc powtórna
podmiana dałaby `astroklient-demo-demo`. Sprawdziłem najpierw, że nigdzie nie ma
jeszcze nowej nazwy, i dopiero wtedy podmieniłem raz.

Polska odmiana. Ślepa podmiana zamieniła „astroklienta" na „astroklient-demoa”
w czterech miejscach; poprawione na „astroklienta-demo". Tytuł FastAPI wyszedłby
jako „astroklient-demo · demo", a nazwa jobu jako „Testy astroklienta-demo
(wersja demo)" — oba skrócone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 23:36:55 +02:00
parent caf4fd80d1
commit 16cf25468f
17 changed files with 30 additions and 30 deletions
+3 -3
View File
@@ -9,12 +9,12 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Login - name: Login
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.czernobog.pl -u gitea --password-stdin run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.czernobog.pl -u gitea --password-stdin
# astroklient dołącza do tej samej pętli: dzieli warstwę logiczną i łącze, # astroklient-demo dołącza do tej samej pętli: dzieli warstwę logiczną i łącze,
# więc jego obraz ma powstawać z tego samego commita co reszta produktu. # więc jego obraz ma powstawać z tego samego commita co reszta produktu.
- name: Build & push (data, logic, presentation, astroklient) - name: Build & push (data, logic, presentation, astroklient-demo)
run: | run: |
TAG=${GITHUB_SHA::8} TAG=${GITHUB_SHA::8}
for SVC in data logic presentation astroklient; do for SVC in data logic presentation astroklient-demo; do
docker build -t gitea.czernobog.pl/gitea/astrololo-$SVC:$TAG ./services/$SVC docker build -t gitea.czernobog.pl/gitea/astrololo-$SVC:$TAG ./services/$SVC
docker push gitea.czernobog.pl/gitea/astrololo-$SVC:$TAG docker push gitea.czernobog.pl/gitea/astrololo-$SVC:$TAG
done done
+5 -5
View File
@@ -91,8 +91,8 @@ jobs:
PYTHONPATH: . PYTHONPATH: .
run: pytest tests -q -rs run: pytest tests -q -rs
astroklient-tests: astroklient-demo-tests:
name: Testy astroklienta (wersja demo) name: Testy astroklienta-demo
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -100,14 +100,14 @@ jobs:
with: with:
python-version: "3.12" python-version: "3.12"
cache: pip cache: pip
cache-dependency-path: services/astroklient/requirements-dev.txt cache-dependency-path: services/astroklient-demo/requirements-dev.txt
- name: Instalacja zależności - name: Instalacja zależności
run: pip install -r services/astroklient/requirements-dev.txt run: pip install -r services/astroklient-demo/requirements-dev.txt
# Demo rozmawia z warstwą danych PRODUKCJI, więc jego powierzchnia musi być # Demo rozmawia z warstwą danych PRODUKCJI, więc jego powierzchnia musi być
# pilnowana tak samo jak reszty: testy sprawdzają m.in., że nie przybyła # pilnowana tak samo jak reszty: testy sprawdzają m.in., że nie przybyła
# żadna trasa poza dwiema funkcjami. # żadna trasa poza dwiema funkcjami.
- name: Testy (pytest) - name: Testy (pytest)
working-directory: services/astroklient working-directory: services/astroklient-demo
env: env:
PYTHONPATH: . PYTHONPATH: .
run: pytest tests -q -rs run: pytest tests -q -rs
@@ -1,4 +1,4 @@
# astroklient — wersja demonstracyjna (PRE-28) # astroklient-demo — wersja demonstracyjna (PRE-28)
Osobna warstwa prezentacji o **dwóch funkcjach**: Osobna warstwa prezentacji o **dwóch funkcjach**:
@@ -18,7 +18,7 @@ okazji kodu reszty programu.
## Izolacja: własne dane, pula na konto ## Izolacja: własne dane, pula na konto
``` ```
astroklient → logic-demo → data-demo → udział astrololo-demo (pusty na starcie) astroklient-demo → logic-demo → data-demo → udział astrololo-demo (pusty na starcie)
└── klientA/ ← pula konta └── klientA/ ← pula konta
└── klientB/ ← pula konta └── klientB/ ← pula konta
``` ```
@@ -42,7 +42,7 @@ w nazwie katalogu: `../..` czy ukośnik wyprowadziłyby zapytanie poza udział.
**Pula klienta to jeden katalog**, więc przejście na pełną wersję nie oznacza **Pula klienta to jeden katalog**, więc przejście na pełną wersję nie oznacza
utraty wgrań — procedura importu jest w runbooku wdrożenia (repo `deploy`, utraty wgrań — procedura importu jest w runbooku wdrożenia (repo `deploy`,
`astrololo/README-astroklient.md`). `astrololo/README-astroklient-demo.md`).
## Konfiguracja ## Konfiguracja
@@ -66,7 +66,7 @@ cd services/presentation && python scripts/make_user.py demo
## Uruchomienie lokalne ## Uruchomienie lokalne
```bash ```bash
cd services/astroklient cd services/astroklient-demo
pip install -r requirements-dev.txt pip install -r requirements-dev.txt
DEMO_PASSWORD=tajne LOGIC_URL=http://localhost:8001 \ DEMO_PASSWORD=tajne LOGIC_URL=http://localhost:8001 \
uvicorn app.main:app --port 8005 uvicorn app.main:app --port 8005
@@ -75,7 +75,7 @@ DEMO_PASSWORD=tajne LOGIC_URL=http://localhost:8001 \
Testy: Testy:
```bash ```bash
cd services/astroklient && PYTHONPATH=. pytest tests -q cd services/astroklient-demo && PYTHONPATH=. pytest tests -q
``` ```
## Co pilnują testy ## Co pilnują testy
@@ -45,7 +45,7 @@ class LogicClient:
def report(self, when_utc_iso: str, lat: float, lon: float) -> dict[str, Any]: def report(self, when_utc_iso: str, lat: float, lon: float) -> dict[str, Any]:
"""Interpretacja urodzeniowa: sygnifikatory z obliczeń szukane w bazach. """Interpretacja urodzeniowa: sygnifikatory z obliczeń szukane w bazach.
Jedyne pytanie, jakie astroklient zadaje o treść bez grupowania, Jedyne pytanie, jakie astroklient-demo zadaje o treść bez grupowania,
bez promptów, bez modelu językowego.""" bez promptów, bez modelu językowego."""
payload = {"when_utc": when_utc_iso, "lat": lat, "lon": lon, payload = {"when_utc": when_utc_iso, "lat": lat, "lon": lon,
"limit": settings.report_limit, "group": False, "limit": settings.report_limit, "group": False,
@@ -1,4 +1,4 @@
"""Konfiguracja astroklienta — wersji demonstracyjnej (PRE-28). """Konfiguracja astroklienta-demo — wersji demonstracyjnej (PRE-28).
Zna WYŁĄCZNIE adres warstwy logicznej, tak samo jak pełna prezentacja. Nie wie Zna WYŁĄCZNIE adres warstwy logicznej, tak samo jak pełna prezentacja. Nie wie
nic o bazach, Excelu ani SQL-u. nic o bazach, Excelu ani SQL-u.
@@ -1,4 +1,4 @@
"""astroklient — wersja DEMONSTRACYJNA (PRE-28). """astroklient-demo — wersja DEMONSTRACYJNA (PRE-28).
DWIE FUNKCJE I ANI JEDNEJ WIĘCEJ: DWIE FUNKCJE I ANI JEDNEJ WIĘCEJ:
1. dodanie pliku bazy (wgranie + włączenie do użytku), 1. dodanie pliku bazy (wgranie + włączenie do użytku),
@@ -10,7 +10,7 @@ Tutaj pozostałych funkcji NIE MA W OBRAZIE: nie ma tras, nie ma szablonów, nie
nawet metod w kliencie warstwy logicznej. Demo można komuś oddać, nie oddając nawet metod w kliencie warstwy logicznej. Demo można komuś oddać, nie oddając
przy okazji kodu reszty programu. przy okazji kodu reszty programu.
UWAGA, ŚWIADOMA DECYZJA WŁAŚCICIELA: astroklient pracuje na TEJ SAMEJ warstwie UWAGA, ŚWIADOMA DECYZJA WŁAŚCICIELA: astroklient-demo pracuje na TEJ SAMEJ warstwie
danych co produkcja. Kto ma do niego dostęp, czyta oryginalne bazy interpretacyjne, danych co produkcja. Kto ma do niego dostęp, czyta oryginalne bazy interpretacyjne,
a pliki przez niego wgrane trafiają do produkcyjnego zbioru. Dlatego konto jest a pliki przez niego wgrane trafiają do produkcyjnego zbioru. Dlatego konto jest
osobne (DEMO_USER/DEMO_PASSWORD) żeby dało się odciąć demo jedną zmienną, bez osobne (DEMO_USER/DEMO_PASSWORD) żeby dało się odciąć demo jedną zmienną, bez
@@ -32,7 +32,7 @@ from app.clients.logic_client import LogicClient
# Automatyczna dokumentacja WYŁĄCZONA — tak samo jak w pełnej aplikacji. /docs # Automatyczna dokumentacja WYŁĄCZONA — tak samo jak w pełnej aplikacji. /docs
# wypisałoby komplet tras, a demo ma nie zdradzać nawet własnej powierzchni. # wypisałoby komplet tras, a demo ma nie zdradzać nawet własnej powierzchni.
app = FastAPI(title="astroklient · demo", docs_url=None, redoc_url=None, openapi_url=None) app = FastAPI(title="astroklient-demo", docs_url=None, redoc_url=None, openapi_url=None)
security.install(app) security.install(app)
app.mount("/static", StaticFiles(directory="app/static"), name="static") app.mount("/static", StaticFiles(directory="app/static"), name="static")
@@ -48,7 +48,7 @@ def _logic(request: Request) -> LogicClient:
return LogicClient(tenant=getattr(request.state, "user", "") or "") return LogicClient(tenant=getattr(request.state, "user", "") or "")
# Stan pliku „w użyciu" — ta sama nazwa, co w warstwie danych (DAN-27). Wpisana # Stan pliku „w użyciu" — ta sama nazwa, co w warstwie danych (DAN-27). Wpisana
# tutaj wprost, bo astroklient nie importuje tamtej warstwy; literówka objawiłaby # tutaj wprost, bo astroklient-demo nie importuje tamtej warstwy; literówka objawiłaby
# się dopiero na żywo, więc pilnuje jej test. # się dopiero na żywo, więc pilnuje jej test.
ACTIVE = "active" ACTIVE = "active"
@@ -152,4 +152,4 @@ def _wroc(error: str = "", done: str = "") -> RedirectResponse:
@app.get("/health") @app.get("/health")
def health() -> dict: def health() -> dict:
return {"status": "ok", "layer": "astroklient"} return {"status": "ok", "layer": "astroklient-demo"}
@@ -1,4 +1,4 @@
"""Logowanie do astroklienta (PRE-28). """Logowanie do astroklienta-demo (PRE-28).
OSOBNE KONTO, NIE WSPÓŁDZIELONE Z GŁÓWNĄ APLIKACJĄ. Demo pracuje na tej samej OSOBNE KONTO, NIE WSPÓŁDZIELONE Z GŁÓWNĄ APLIKACJĄ. Demo pracuje na tej samej
warstwie danych co produkcja, więc kto ma do niego dostęp, czyta oryginalne bazy warstwie danych co produkcja, więc kto ma do niego dostęp, czyta oryginalne bazy
@@ -26,8 +26,8 @@ from collections import deque
from fastapi import Request from fastapi import Request
from fastapi.responses import HTMLResponse, JSONResponse from fastapi.responses import HTMLResponse, JSONResponse
log = logging.getLogger("astroklient.security") log = logging.getLogger("astroklient-demo.security")
audit_log = logging.getLogger("astroklient.audit") audit_log = logging.getLogger("astroklient-demo.audit")
_SCRYPT_N, _SCRYPT_R, _SCRYPT_P, _SCRYPT_LEN = 2**14, 8, 1, 32 _SCRYPT_N, _SCRYPT_R, _SCRYPT_P, _SCRYPT_LEN = 2**14, 8, 1, 32
_HASH_PREFIX = "scrypt$" _HASH_PREFIX = "scrypt$"
@@ -163,7 +163,7 @@ def install(app) -> None:
_setup_audit_logging() _setup_audit_logging()
if not auth_enabled(): if not auth_enabled():
log.warning( log.warning(
"UWAGA: brak kont (DEMO_USERS / DEMO_PASSWORD) — astroklient stoi OTWARTY. " "UWAGA: brak kont (DEMO_USERS / DEMO_PASSWORD) — astroklient-demo stoi OTWARTY. "
"Bez logowania nie ma też PULI: wszyscy pracowaliby na jednym, wspólnym " "Bez logowania nie ma też PULI: wszyscy pracowaliby na jednym, wspólnym "
"zbiorze baz, widząc nawzajem swoje wgrania." "zbiorze baz, widząc nawzajem swoje wgrania."
) )
@@ -184,7 +184,7 @@ def install(app) -> None:
if not user: if not user:
return HTMLResponse( return HTMLResponse(
"<h1>401 — wymagane logowanie</h1>", status_code=401, "<h1>401 — wymagane logowanie</h1>", status_code=401,
headers={"WWW-Authenticate": 'Basic realm="astroklient"'}) headers={"WWW-Authenticate": 'Basic realm="astroklient-demo"'})
request.state.user = user request.state.user = user
response = await call_next(request) response = await call_next(request)
@@ -1,4 +1,4 @@
/* astroklient arkusz CELOWO minimalny. Nie kopiujemy stylów pełnej aplikacji: /* astroklient-demo arkusz CELOWO minimalny. Nie kopiujemy stylów pełnej aplikacji:
demo ma dwa formularze i tabelę wyników, a każda przeniesiona klasa niosłaby demo ma dwa formularze i tabelę wyników, a każda przeniesiona klasa niosłaby
nazwy funkcji, których tu nie ma. */ nazwy funkcji, których tu nie ma. */
:root { :root {
@@ -3,13 +3,13 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>astroklient · {% block title %}{% endblock %}</title> <title>astroklient-demo · {% block title %}{% endblock %}</title>
<link rel="stylesheet" href="/static/styles.css"> <link rel="stylesheet" href="/static/styles.css">
</head> </head>
<body> <body>
<main> <main>
<header class="topbar"> <header class="topbar">
<h1>astroklient</h1> <h1>astroklient-demo</h1>
{# CELOWO BEZ NAWIGACJI. Dwie funkcje mieszczą się na jednym ekranie, {# CELOWO BEZ NAWIGACJI. Dwie funkcje mieszczą się na jednym ekranie,
a pasek zakładek z jedną pozycją tylko sugerowałby, że są inne. #} a pasek zakładek z jedną pozycją tylko sugerowałby, że są inne. #}
</header> </header>
@@ -1,4 +1,4 @@
"""astroklient — wersja demonstracyjna (PRE-28). """astroklient-demo — wersja demonstracyjna (PRE-28).
DWIE WŁASNOŚCI, KTÓRYCH PILNUJE TEN PLIK DWIE WŁASNOŚCI, KTÓRYCH PILNUJE TEN PLIK
@@ -104,7 +104,7 @@ def test_without_credentials_nothing_is_served(env, monkeypatch):
def test_demo_account_is_separate_from_the_main_application(env, monkeypatch): def test_demo_account_is_separate_from_the_main_application(env, monkeypatch):
"""Konto astroklienta MUSI być własne: demo pracuje na tej samej warstwie """Konto astroklienta-demo MUSI być własne: demo pracuje na tej samej warstwie
danych co produkcja, więc odcięcie go nie może wymagać zmiany haseł danych co produkcja, więc odcięcie go nie może wymagać zmiany haseł
w głównej aplikacji.""" w głównej aplikacji."""
monkeypatch.setenv("APP_USER", "astrololo") monkeypatch.setenv("APP_USER", "astrololo")