Dawarich: the third new app through the checklist — template, record, fixture, first ladder step 1.15.2 -> 1.15.3
gates / gates (push) Successful in 3s
gates / gates (push) Successful in 3s
Location history with PostGIS 17 + Redis + Sidekiq. The seeded known login replaced by after_install behind the install hold; geocoding off; SECRET_KEY_BASE a data_key; smtp_mapping with mail-off boot measured. onboarding/dawarich.md complete. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -224,6 +224,29 @@
|
||||
"description": "Minecraft szerver kezelő webes felülettel",
|
||||
"initial_credentials.note": "Kezdeti admin jelszó (a telepítéskor beállított). Az első belépés után változtasd meg a Crafty felületén — ez a kártya továbbra is a kezdeti jelszót mutatja."
|
||||
},
|
||||
"dawarich": {
|
||||
"app_info.add_people": "A családtagod fiókját te hozod létre a Beállítások, Felhasználók oldalon; mindenki a saját telefonjáról küldi a helyét.",
|
||||
"app_info.default_creds": "demo@dawarich.app / safepassword",
|
||||
"app_info.first_steps[0]": "Nyisd meg a timeline.DOMAIN címet, és jelentkezz be: demo@dawarich.app és a Beállítások oldalon látható jelszó",
|
||||
"app_info.first_steps[1]": "A fiókod beállításaiban cseréld le az e-mail-címet a sajátodra - a demo@dawarich.app név nyilvános",
|
||||
"app_info.first_steps[2]": "A beállításokban másold ki az API-kulcsodat",
|
||||
"app_info.first_steps[3]": "Telepítsd a Dawarich alkalmazást a telefonodra, és add meg: https://timeline.DOMAIN és az API-kulcsot",
|
||||
"app_info.first_steps[4]": "A térkép csempéit a böngésződ a Dawarich térképszerveréről tölti le; a helyadataid a te szervereden maradnak",
|
||||
"app_info.tagline": "Saját helyelőzmények - a telefonod a te szerveredre küldi, hol jártál",
|
||||
"app_info.use_cases[0]": "A Google Idővonal helyett: a helyelőzményeid a saját szervereden",
|
||||
"app_info.use_cases[1]": "Térkép, statisztika és utazások az összes helyről, ahol jártál",
|
||||
"app_info.use_cases[2]": "Google Takeout, GPX és GeoJSON import a régi adataidhoz",
|
||||
"app_info.use_cases[3]": "A Dawarich, az Overland vagy az OwnTracks alkalmazás küldi a telefonod helyét",
|
||||
"deploy_fields[ADMIN_PASSWORD].description": "Az első bejelentkezéshez: demo@dawarich.app és ez a jelszó. A Beállításokban cseréld le az e-mail-címet a sajátodra.",
|
||||
"deploy_fields[ADMIN_PASSWORD].label": "Bejelentkezési jelszó (demo@dawarich.app)",
|
||||
"deploy_fields[DB_PASSWORD].label": "Adatbázis jelszó",
|
||||
"deploy_fields[DOMAIN].description": "A szerver domain neve",
|
||||
"deploy_fields[DOMAIN].label": "Domain",
|
||||
"deploy_fields[SECRET_KEY_BASE].label": "Titkosítási kulcs",
|
||||
"deploy_fields[SUBDOMAIN].description": "Az alkalmazás aldomainje",
|
||||
"deploy_fields[SUBDOMAIN].label": "Aldomain",
|
||||
"description": "Saját helyelőzmények térképen - a Google Idővonal helyett"
|
||||
},
|
||||
"docmost": {
|
||||
"app_info.first_steps[0]": "Nyisd meg a docs.DOMAIN címet a böngészőben",
|
||||
"app_info.first_steps[1]": "Az első regisztrált felhasználó automatikusan admin lesz",
|
||||
@@ -1205,6 +1228,7 @@
|
||||
}
|
||||
},
|
||||
"reasons": {
|
||||
"dawarich": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules",
|
||||
"karakeep": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules",
|
||||
"radicale": "new app 2026-10-01 through NEW-APP-CHECKLIST.md: te-form, no kérjük; reviewed by CC against the operator rules"
|
||||
}
|
||||
|
||||
@@ -2003,6 +2003,69 @@ class Karakeep:
|
||||
return ok
|
||||
|
||||
|
||||
# =============================================================================================
|
||||
class Dawarich:
|
||||
"""Dawarich (2026-10-01, new app through NEW-APP-CHECKLIST.md). THE FRONT DOOR is the route the phone apps use:
|
||||
`POST /api/v1/overland/batches?api_key=<key>` (Overland's GeoJSON — one point with a unique place and time), read back
|
||||
with `GET /api/v1/points?api_key=…&start_at=…&end_at=…`. The API key is the account's own (the household copies it
|
||||
from its settings page); the fixture reads it with the app's own CLI (`bin/rails runner` in the app's container —
|
||||
R-156's allowed route, nothing planted). Negative controls on every readback: no key 401, a wrong key 401, a range
|
||||
with nothing in it answers []."""
|
||||
sub = "timeline"
|
||||
|
||||
@staticmethod
|
||||
def _key(w):
|
||||
out = w.guest("docker exec dawarich bin/rails runner 'puts User.order(:id).first.api_key' 2>/dev/null | tail -1")
|
||||
k = (out or "").strip()
|
||||
return k if re.fullmatch(r"[A-Za-z0-9_-]{20,}", k) else None
|
||||
|
||||
def seed(self, w, sub, say):
|
||||
if not w.wait_app(sub, "/api/v1/health", want=("200",), tries=90):
|
||||
self.tried = "/api/v1/health never answered 200"
|
||||
return None
|
||||
key = self._key(w)
|
||||
if not key:
|
||||
self.tried = "no API key from the app's own CLI"
|
||||
return None
|
||||
lat = round(47.40 + secrets.randbelow(2000) / 10000.0, 4)
|
||||
lon = round(19.00 + secrets.randbelow(2000) / 10000.0, 4)
|
||||
ts = "2026-09-%02dT%02d:%02d:00Z" % (1 + secrets.randbelow(28), secrets.randbelow(24), secrets.randbelow(60))
|
||||
body = {"locations": [{"type": "Feature", "geometry": {"type": "Point", "coordinates": [lon, lat]},
|
||||
"properties": {"timestamp": ts, "altitude": 110, "speed": 0, "horizontal_accuracy": 5,
|
||||
"device_id": "felhom-fixture"}}]}
|
||||
rc, code, out = w.app_curl(sub, f"/api/v1/overland/batches?api_key={key}", "-H", "Content-Type: application/json",
|
||||
data=json.dumps(body), method="POST")
|
||||
say(f" dawarich: POST /api/v1/overland/batches http={code} ({lat},{lon} at {ts})")
|
||||
if code not in ("200", "201"):
|
||||
self.tried = f"overland batch -> {code} {(out or '')[:120]}"
|
||||
return None
|
||||
return {"lat": lat, "lon": lon, "ts": ts}
|
||||
|
||||
def verify(self, w, sub, t, say):
|
||||
if not w.wait_app(sub, "/api/v1/health", want=("200",), tries=90):
|
||||
say(" dawarich: /api/v1/health never answered")
|
||||
return False
|
||||
key = self._key(w)
|
||||
day = t["ts"][:10]
|
||||
rng = f"start_at={day}T00:00:00Z&end_at={day}T23:59:59Z"
|
||||
rc, c_none, _ = w.app_curl(sub, f"/api/v1/points?{rng}")
|
||||
rc, c_wrong, _ = w.app_curl(sub, f"/api/v1/points?api_key=felhom-wrong-key&{rng}")
|
||||
rc, c_empty, o_empty = w.app_curl(sub, f"/api/v1/points?api_key={key}&start_at=2001-01-01T00:00:00Z&end_at=2001-01-02T00:00:00Z")
|
||||
if c_none != "401" or c_wrong != "401" or (o_empty or "").strip() != "[]":
|
||||
say(f" dawarich: READBACK UNUSABLE — no key {c_none}, wrong key {c_wrong}, empty range {(o_empty or '')[:40]!r}")
|
||||
return False
|
||||
found = False
|
||||
for _ in range(12): # the batch is stored by a background job
|
||||
rc, code, out = w.app_curl(sub, f"/api/v1/points?api_key={key}&{rng}")
|
||||
# the point's fields are read as text: the unique coordinates must both appear in the answer
|
||||
found = code == "200" and str(t["lat"]) in (out or "") and str(t["lon"]) in (out or "")
|
||||
if found:
|
||||
break
|
||||
time.sleep(5)
|
||||
say(f" dawarich: readback http={code} found={found} (controls: no key {c_none}, wrong {c_wrong}, empty range [])")
|
||||
return found
|
||||
|
||||
|
||||
FIXTURES = {
|
||||
"uptime-kuma": UptimeKuma(),
|
||||
"crafty-controller": Crafty(),
|
||||
@@ -2041,4 +2104,5 @@ FIXTURES = {
|
||||
"calcom": Calcom(),
|
||||
"radicale": Radicale(),
|
||||
"karakeep": Karakeep(),
|
||||
"dawarich": Dawarich(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user