diff --git a/README.md b/README.md index 8b04227..0f5b45d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# lazyflat +# wohnungsdidi Combined deployment of **flat-alert** (reliable scraper) and **flat-apply** (experimental auto‑applier) 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/lazyflat.git`. +1. **Create repo**: push this monorepo to `ssh://git@git.moritz.run:2222/moritz/wohnungsdidi.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 diff --git a/alert/main.py b/alert/main.py index 6dad3a2..42b4b66 100644 --- a/alert/main.py +++ b/alert/main.py @@ -92,7 +92,7 @@ class FlatAlerter: if __name__ == "__main__": - logger.info("starting lazyflat alert service") + logger.info("starting wohnungsdidi alert service") alerter = FlatAlerter() while True: try: diff --git a/apply/main.py b/apply/main.py index ea1ec1f..de20496 100644 --- a/apply/main.py +++ b/apply/main.py @@ -81,7 +81,7 @@ async def lifespan(_app: FastAPI): yield -app = FastAPI(lifespan=lifespan, title="lazyflat-apply", docs_url=None, redoc_url=None) +app = FastAPI(lifespan=lifespan, title="wohnungsdidi-apply", docs_url=None, redoc_url=None) @app.get("/health") diff --git a/docker-compose.yml b/docker-compose.yml index 15b42a3..016b515 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: web: build: ./web - container_name: lazyflat-web + container_name: wohnungsdidi-web restart: unless-stopped depends_on: apply: @@ -26,18 +26,20 @@ services: - SMTP_PORT=${SMTP_PORT:-587} - SMTP_USERNAME=${SMTP_USERNAME:-} - SMTP_PASSWORD=${SMTP_PASSWORD:-} - - SMTP_FROM=${SMTP_FROM:-lazyflat@localhost} + - SMTP_FROM=${SMTP_FROM:-wohnungsdidi@localhost} - SMTP_STARTTLS=${SMTP_STARTTLS:-true} - 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: lazyflat-apply + container_name: wohnungsdidi-apply restart: unless-stopped expose: - "8000" @@ -51,7 +53,7 @@ services: alert: build: ./alert - container_name: lazyflat-alert + container_name: wohnungsdidi-alert restart: unless-stopped depends_on: web: diff --git a/web/app.py b/web/app.py index 8430a43..1730bd8 100644 --- a/web/app.py +++ b/web/app.py @@ -1,5 +1,5 @@ """ -lazyflat web app. +wohnungsdidi web app. Tabs: - / → Wohnungen @@ -84,7 +84,7 @@ async def lifespan(_app: FastAPI): yield -app = FastAPI(lifespan=lifespan, title="lazyflat", docs_url=None, redoc_url=None, openapi_url=None) +app = FastAPI(lifespan=lifespan, title="wohnungsdidi", docs_url=None, redoc_url=None, openapi_url=None) app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") @@ -712,7 +712,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"lazyflat application report\n" + f"wohnungsdidi application report\n" f"application_id={a['id']}\n" f"flat_id={a['flat_id']}\n" f"provider={a['provider']}\n" @@ -764,7 +764,7 @@ def bewerbung_zip(request: Request, app_id: int): zf.writestr(f"snapshots/{idx:02d}_{label}.html", html) buf.seek(0) - filename = f"lazyflat-report-{a['id']}.zip" + filename = f"wohnungsdidi-report-{a['id']}.zip" return StreamingResponse( buf, media_type="application/zip", headers={"Content-Disposition": f'attachment; filename="{filename}"'}, @@ -889,7 +889,7 @@ def tab_logs_export(request: Request): e["ip"], ]) body = buf.getvalue().encode("utf-8") - filename = "lazyflat-protokoll" + filename = "wohnungsdidi-protokoll" if q.get("from"): filename += f"-{q['from']}" if q.get("to"): filename += f"-bis-{q['to']}" filename += ".csv" diff --git a/web/db.py b/web/db.py index 0f9d9a8..362f162 100644 --- a/web/db.py +++ b/web/db.py @@ -1,5 +1,5 @@ """ -SQLite data layer for lazyflat. +SQLite data layer for wohnungsdidi. Multi-user: users, per-user profiles/filters/notifications/preferences. All per-user rows are 1:1 with users. Errors and forensics are retained diff --git a/web/enrichment.py b/web/enrichment.py index 7e8b103..3429b32 100644 --- a/web/enrichment.py +++ b/web/enrichment.py @@ -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 (lazyflat enricher)"}, + "User-Agent": "Mozilla/5.0 (wohnungsdidi enricher)"}, timeout=IMAGE_TIMEOUT, stream=True, ) diff --git a/web/notifications.py b/web/notifications.py index 2bc3ffa..9c16caf 100644 --- a/web/notifications.py +++ b/web/notifications.py @@ -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="[lazyflat] passende Wohnung", body_plain=body, body_markdown=md) + notify_user(user_id, "match", subject="[wohnungsdidi] 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="[lazyflat] Bewerbung OK", body_plain=body, body_markdown=md) + notify_user(user_id, "apply_ok", subject="[wohnungsdidi] 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}/fehler" md = (f"*Bewerbung fehlgeschlagen*\n{addr}\n{message}\n" f"[Fehler ansehen]({PUBLIC_URL}/fehler)") - notify_user(user_id, "apply_fail", subject="[lazyflat] Bewerbung fehlgeschlagen", + notify_user(user_id, "apply_fail", subject="[wohnungsdidi] Bewerbung fehlgeschlagen", body_plain=body, body_markdown=md) diff --git a/web/settings.py b/web/settings.py index 2eaef8b..ca95511 100644 --- a/web/settings.py +++ b/web/settings.py @@ -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 = "lazyflat_session" +SESSION_COOKIE_NAME: str = "wohnungsdidi_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,6 +43,8 @@ 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. @@ -58,7 +60,7 @@ SMTP_HOST: str = getenv("SMTP_HOST", "") SMTP_PORT: int = int(getenv("SMTP_PORT", "587")) SMTP_USERNAME: str = getenv("SMTP_USERNAME", "") SMTP_PASSWORD: str = getenv("SMTP_PASSWORD", "") -SMTP_FROM: str = getenv("SMTP_FROM", "lazyflat@localhost") +SMTP_FROM: str = getenv("SMTP_FROM", "wohnungsdidi@localhost") SMTP_STARTTLS: bool = getenv("SMTP_STARTTLS", "true").lower() in ("true", "1", "yes", "on") # --- App URL (used to build links in notifications) --------------------------- diff --git a/web/static/app.js b/web/static/app.js index d8c4762..bae21c2 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -19,9 +19,7 @@ function fmtCountUp(iso) { const ts = Date.parse(iso); if (!iso || Number.isNaN(ts)) return "—"; const diff = Math.max(0, Math.floor((Date.now() - ts) / 1000)); - if (diff < 60) return `vor ${diff} s`; - if (diff < 3600) return `vor ${Math.floor(diff / 60)} min`; - return `vor ${Math.floor(diff / 3600)} h`; + return `vor ${diff} s`; } function updateRelativeTimes() { diff --git a/web/static/didi.webp b/web/static/didi.webp new file mode 100644 index 0000000..551f3d9 Binary files /dev/null and b/web/static/didi.webp differ diff --git a/web/templates/_layout.html b/web/templates/_layout.html index ffdb4be..4a0e94d 100644 --- a/web/templates/_layout.html +++ b/web/templates/_layout.html @@ -7,8 +7,8 @@
-
-

lazyflat

+ +

wohnungsdidi

{{ user.username }}{% if is_admin %} · Administrator{% endif %} @@ -29,9 +29,7 @@
{% block content %}{% endblock %}
-{% if user.username == 'annika' %}
Programmiert für Annika ♥
-{% endif %} {% endblock %} diff --git a/web/templates/base.html b/web/templates/base.html index 8f9bda7..b31c924 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -4,7 +4,7 @@ - {% block title %}lazyflat{% endblock %} + {% block title %}wohnungsdidi{% endblock %}
-
+
-

lazyflat

+

wohnungsdidi

Anmeldung erforderlich