Needed for the MinIO storage flip; unset values fall back to the
Tigris defaults in settings.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Defaults unchanged (Tigris virtual-host style); MinIO needs
path-style and a custom public host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Web (runbolt) + Celery worker from one image; Postgres stays a
Dokploy-managed database reached over dokploy-network.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The infra section had drifted: claimed two processes (web + worker)
and 4GB dedicated Postgres, neither of which has been true for weeks.
Also documents the Tigris CORS config and the daily-restart workflow
so future sessions know they exist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wedges resumed despite the April 16 DB-timeout fix, and this time with
zero ESTABLISHED connections to Postgres — so not a DB deadlock. Most
suspicious piece is DbRetryMiddleware itself: its async path awaits
sync_to_async(...) inside an exception handler, a known-tricky pattern
on the asgiref thread pool. Django already handles stale-connection
recovery via conn_health_checks=True plus the psycopg connect_timeout/
statement_timeout added earlier, so the retry layer isn't load-bearing.
Also:
- RequestLoggingMiddleware is now async-capable, so the full chain
stays async for async views instead of hopping through the thread
pool on every request.
- Bump django-bolt 0.7.4 → 0.7.5 for any upstream fixes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two problems caused the whole app to hang after DB restarts, not just
return errors:
1. psycopg had no timeouts, so in-flight queries blocked forever on a
half-open TCP socket after the Postgres backend died. That starved
the ASGI worker pool — even /health queued behind stuck threads.
Adds connect_timeout, TCP keepalives, and server-side
statement_timeout so broken connections surface as errors in
seconds rather than hanging indefinitely.
2. DbRetryMiddleware was sync-only; django-bolt mounts Django under
ASGI, so async views never actually went through the retry path.
It also used close_old_connections() which respects CONN_MAX_AGE
and leaves freshly-broken connections in place. Rewritten with
sync+async support and an explicit conn.close() on all connections
before retry. Also catches InterfaceError (psycopg3 closed-conn).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mitigates long-running app wedges (e.g. request-loop deadlocks that
leave the machine 'started' but unresponsive). Runs at 08:00 UTC with
manual dispatch available.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers (visibility, is_processing, upload_date) which is the
primary filter used across the site.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Catches OperationalError from stale connections, closes them,
and retries the request once. Prevents 500s after DB restarts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OOM crash took the site down — increase memory from 1GB to 2GB.
Add Crawl-delay to robots.txt to throttle aggressive bots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
/random/ picks a random public image and redirects to its
detail page. Shows as the last nav item after Search.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Inter font via Google Fonts with proper preconnect
- Nav: backdrop-filter blur with semi-transparent background
- Image cards: hover-reveal overlay, inner zoom, shadow on hover
- Gear/collection cards: hover lift with shadow
- EXIF bar: rounded card style instead of bordered lines
- Search inputs: accent focus glow ring
- Homepage: display site tagline, more generous hero spacing
- View Transitions API for smooth cross-page fades (Chrome 126+)
- Animated loading pulse for infinite scroll
- Global: smooth scroll, accent selection color, focus-visible
- Radius 6px→8px, font smoothing, CSS custom properties
- Fix stray </div> in base template
- Disable hover transforms on mobile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prev on left, download centered, next on right — all directly
below the image for easy navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show photo count as badge overlaid on collection card images
- Move prev/next navigation to appear after EXIF/tags, before map
- Download dropdown now appears last
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ASGI threads each hold a connection; with conn_max_age=60 they pile up
and exhaust Postgres max_connections on the shared 2GB instance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Postgres broker eats all DB connections. Thread fallback handles
image processing and AI descriptions inline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
oEmbed now returns an iframe pointing to /embed/ — full JS interactivity
like SoundCloud. Year filtering, lazy loading, load more button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>