refactor: rename wohnungsdidi → lazyflat

Container names, FastAPI titles, email subjects, filenames, brand text,
session cookie, User-Agent, docstrings, README. Volume lazyflat_data and
/data/lazyflat.sqlite already used the new name, so on-disk data is
preserved; dropped the now-obsolete legacy-rename comments.

Side effect: SESSION_COOKIE_NAME change logs everyone out on deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-23 09:26:12 +02:00
parent f1e26b38d0
commit d06dfdaca1
15 changed files with 22 additions and 26 deletions

View file

@ -1,4 +1,4 @@
# wohnungsdidi
# lazyflat
Combined deployment of **flat-alert** (reliable scraper) and **flat-apply**
(experimental autoapplier) behind a single authenticated web UI.
@ -51,7 +51,7 @@ Because `apply/` is still experimental, the system is hardened around it:
## Deployment on Coolify
1. **Create repo**: push this monorepo to `ssh://git@git.moritz.run:2222/moritz/wohnungsdidi.git`.
1. **Create repo**: push this monorepo to `ssh://git@git.moritz.run:2222/moritz/lazyflat.git`.
2. **New Coolify resource***Docker Compose* → point it at this repo. Coolify
will read `docker-compose.yml` and deploy all three services on one network.
3. **Domain**: set `flat.lab.moritz.run` on the `web` service only. Coolify

View file

@ -87,7 +87,7 @@ class FlatAlerter:
if __name__ == "__main__":
logger.info("starting wohnungsdidi alert service")
logger.info("starting lazyflat alert service")
alerter = FlatAlerter()
while True:
try:

View file

@ -82,7 +82,7 @@ async def lifespan(_app: FastAPI):
yield
app = FastAPI(lifespan=lifespan, title="wohnungsdidi-apply", docs_url=None, redoc_url=None)
app = FastAPI(lifespan=lifespan, title="lazyflat-apply", docs_url=None, redoc_url=None)
@app.get("/health")

View file

@ -1,7 +1,7 @@
services:
web:
build: ./web
container_name: wohnungsdidi-web
container_name: lazyflat-web
restart: unless-stopped
depends_on:
apply:
@ -25,15 +25,13 @@ services:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- ANTHROPIC_MODEL=${ANTHROPIC_MODEL:-claude-haiku-4-5-20251001}
volumes:
# Legacy volume name — kept so Coolify reuses the existing data volume
# after the rename from lazyflat → wohnungsdidi.
- lazyflat_data:/data
expose:
- "8000"
apply:
build: ./apply
container_name: wohnungsdidi-apply
container_name: lazyflat-apply
restart: unless-stopped
expose:
- "8000"
@ -47,7 +45,7 @@ services:
alert:
build: ./alert
container_name: wohnungsdidi-alert
container_name: lazyflat-alert
restart: unless-stopped
depends_on:
web:

View file

@ -1,5 +1,5 @@
"""
wohnungsdidi web app.
lazyflat web app.
Tabs:
- / Wohnungen
@ -54,7 +54,7 @@ async def lifespan(_app: FastAPI):
yield
app = FastAPI(lifespan=lifespan, title="wohnungsdidi", docs_url=None, redoc_url=None, openapi_url=None)
app = FastAPI(lifespan=lifespan, title="lazyflat", docs_url=None, redoc_url=None, openapi_url=None)
app.mount("/static", StaticFiles(directory="static"), name="static")

View file

@ -1,5 +1,5 @@
"""
SQLite data layer for wohnungsdidi.
SQLite data layer for lazyflat.
Multi-user: users, per-user profiles/filters/notifications/preferences.
All per-user rows are 1:1 with users. Errors and forensics are retained

View file

@ -127,7 +127,7 @@ def _download_images(flat_id: str, urls: list[str], referer: str) -> int:
r = requests.get(
raw_url,
headers={"Referer": referer,
"User-Agent": "Mozilla/5.0 (wohnungsdidi enricher)"},
"User-Agent": "Mozilla/5.0 (lazyflat enricher)"},
timeout=IMAGE_TIMEOUT,
stream=True,
)

View file

@ -72,14 +72,14 @@ def on_match(user_id: int, flat: dict) -> None:
body = f"Neue passende Wohnung: {addr}\nMiete: {rent}\nZimmer: {rooms}\n{link}"
md = (f"*Neue passende Wohnung*\n[{addr}]({link})\n"
f"Miete: {rent} € · Zimmer: {rooms}\n{PUBLIC_URL}")
notify_user(user_id, "match", subject="[wohnungsdidi] passende Wohnung", body_plain=body, body_markdown=md)
notify_user(user_id, "match", subject="[lazyflat] passende Wohnung", body_plain=body, body_markdown=md)
def on_apply_ok(user_id: int, flat: dict, message: str) -> None:
addr = flat.get("address") or flat.get("link")
body = f"Bewerbung erfolgreich: {addr}\n{message}"
md = f"*Bewerbung erfolgreich*\n{addr}\n{message}"
notify_user(user_id, "apply_ok", subject="[wohnungsdidi] Bewerbung OK", body_plain=body, body_markdown=md)
notify_user(user_id, "apply_ok", subject="[lazyflat] Bewerbung OK", body_plain=body, body_markdown=md)
def on_apply_fail(user_id: int, flat: dict, message: str) -> None:
@ -87,5 +87,5 @@ def on_apply_fail(user_id: int, flat: dict, message: str) -> None:
body = f"Bewerbung fehlgeschlagen: {addr}\n{message}\n{PUBLIC_URL}/bewerbungen"
md = (f"*Bewerbung fehlgeschlagen*\n{addr}\n{message}\n"
f"[Bewerbungen ansehen]({PUBLIC_URL}/bewerbungen)")
notify_user(user_id, "apply_fail", subject="[wohnungsdidi] Bewerbung fehlgeschlagen",
notify_user(user_id, "apply_fail", subject="[lazyflat] Bewerbung fehlgeschlagen",
body_plain=body, body_markdown=md)

View file

@ -136,7 +136,7 @@ def tab_logs_export(request: Request):
e["ip"],
])
body = buf.getvalue().encode("utf-8")
filename = "wohnungsdidi-protokoll"
filename = "lazyflat-protokoll"
if q.get("from"): filename += f"-{q['from']}"
if q.get("to"): filename += f"-bis-{q['to']}"
filename += ".csv"

View file

@ -43,7 +43,7 @@ def bewerbung_zip(request: Request, app_id: int):
with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as zf:
zf.writestr(
"README.txt",
f"wohnungsdidi application report\n"
f"lazyflat application report\n"
f"application_id={a['id']}\n"
f"flat_id={a['flat_id']}\n"
f"provider={a['provider']}\n"
@ -95,7 +95,7 @@ def bewerbung_zip(request: Request, app_id: int):
zf.writestr(f"snapshots/{idx:02d}_{label}.html", html)
buf.seek(0)
filename = f"wohnungsdidi-report-{a['id']}.zip"
filename = f"lazyflat-report-{a['id']}.zip"
return StreamingResponse(
buf, media_type="application/zip",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},

View file

@ -25,7 +25,7 @@ AUTH_PASSWORD_HASH: str = _required("AUTH_PASSWORD_HASH")
# --- Session cookie -----------------------------------------------------------
SESSION_SECRET: str = getenv("SESSION_SECRET") or secrets.token_urlsafe(48)
SESSION_COOKIE_NAME: str = "wohnungsdidi_session"
SESSION_COOKIE_NAME: str = "lazyflat_session"
SESSION_MAX_AGE_SECONDS: int = int(getenv("SESSION_MAX_AGE_SECONDS", str(60 * 60 * 24 * 7)))
COOKIE_SECURE: bool = getenv("COOKIE_SECURE", "true").lower() in ("true", "1", "yes", "on")
@ -43,8 +43,6 @@ ALERT_SCRAPE_INTERVAL_SECONDS: int = int(getenv("ALERT_SCRAPE_INTERVAL_SECONDS",
# --- Storage ------------------------------------------------------------------
DATA_DIR: Path = Path(getenv("DATA_DIR", "/data"))
DATA_DIR.mkdir(parents=True, exist_ok=True)
# Legacy filename — kept so existing data under /data/lazyflat.sqlite stays
# reachable across the rename to wohnungsdidi. Not user-facing.
DB_PATH: Path = DATA_DIR / "lazyflat.sqlite"
# Retention (errors / audit / application forensics). Default 14 days.

View file

@ -1,4 +1,4 @@
/* wohnungsdidi site styling.
/* lazyflat site styling.
Imported via <link> from base.html. Tailwind utilities (via CDN) handle
layout/spacing; this file owns our design tokens and component classes. */

View file

@ -8,7 +8,7 @@
<div class="max-w-6xl mx-auto px-6 py-3 flex items-center justify-between">
<div class="flex items-center gap-3">
<span class="brand-dot"><img src="/static/didi.webp" alt=""></span>
<h1 class="text-xl font-semibold">wohnungsdidi</h1>
<h1 class="text-xl font-semibold">lazyflat</h1>
</div>
<div class="flex items-center gap-4 text-sm">
<span class="text-slate-500">{{ user.username }}{% if is_admin %} · <span class="chip chip-info">Administrator</span>{% endif %}</span>

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<link rel="icon" type="image/webp" href="/static/didi.webp">
<title>{% block title %}wohnungsdidi{% endblock %}</title>
<title>{% block title %}lazyflat{% endblock %}</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"

View file

@ -5,7 +5,7 @@
<div class="flex items-center gap-3 mb-6">
<span class="brand-dot"><img src="/static/didi.webp" alt=""></span>
<div>
<h1 class="text-2xl font-semibold leading-tight">wohnungsdidi</h1>
<h1 class="text-2xl font-semibold leading-tight">lazyflat</h1>
<p class="text-sm text-slate-500">Anmeldung erforderlich</p>
</div>
</div>