S1: shared value helpers (parse_dt/coerce_bool) + auto-Secure session cookie
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 10s
CI & Build / Build & push image (push) Successful in 37s

M9 hardening/DRY pass — section S1, commit 1 (the shared-toolkit foundation):

- Add src/thoughtsync/common.py with parse_dt() and coerce_bool(): one home for
  the ISO-date and truthy-flag coercions that were duplicated across modules.
  notes.py adopts them and deletes _parse_iso_dt, _iso_to_dt and _truthy
  (rule 22 — old copies removed; callers, incl. tests, updated).
- Security: the session cookie is now marked Secure automatically on any request
  that arrived over HTTPS (directly or via a proxy's X-Forwarded-Proto), via a
  SecureCookieSessionInterface override. Hardens HTTPS deployments without
  breaking plain-HTTP LAN installs — no config.

Behavior-preserving refactor + one security hardening. The backend serialization
layer, the json_error sweep, and the notes.py split follow as their own commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-23 19:24:36 -04:00
co-authored by Claude Opus 4.8
parent 3c78060051
commit 2abed7132c
4 changed files with 87 additions and 48 deletions
+35
View File
@@ -0,0 +1,35 @@
from __future__ import annotations
from datetime import datetime
# Small, dependency-free value coercions shared across the blueprints. Kept in one
# place so the "parse an ISO date" / "is this flag truthy" logic has a single
# definition instead of a near-identical copy per module.
def parse_dt(raw: object) -> datetime | None:
"""Parse an ISO-8601 timestamp (accepting a trailing 'Z' for UTC).
Returns None for anything that isn't a non-empty string or doesn't parse, so
callers can treat "absent", "blank", and "malformed" uniformly (a route that
wants a 400 on malformed input checks for None itself).
"""
if not isinstance(raw, str) or not raw:
return None
try:
return datetime.fromisoformat(raw.replace("Z", "+00:00"))
except ValueError:
return None
def coerce_bool(raw: object) -> bool:
"""Truthy for the common flag spellings ('true'/'1'/'yes'/'on', or a real bool).
Used for query-string booleans (?has_reminder=true) and DB-backed bool settings,
which arrive as strings.
"""
if isinstance(raw, bool):
return raw
if isinstance(raw, str):
return raw.strip().lower() in ("true", "1", "yes", "on")
return False