Sign inSign up

toopy/gallery-me

By toopy

•Updated 5 months ago

Self-hosted personal photo gallery — FastAPI + React, single image.

Image
Databases & storage
0

875

toopy/gallery-me repository overview

Self-hosted personal photo gallery. FastAPI + SQLite on the backend, React + graphql-request on the frontend, packaged as a single container serving both /api/* (FastAPI) and /* (the built SPA) on port 8000.

Source: https://gitlab.com/toopy/gallery-me⁠

⁠What's inside

  • Magic-link sign-in (no passwords; SMTP-only) with an optional single-account gate via GALLERY_ACCOUNT_EMAIL.
  • Upload images (JPEG/PNG/WebP/GIF — plus HEIC/HEIF from iPhones, transcoded to JPEG inline) and zip/tar/gz archives.
  • Justified-grid gallery with zoom, filename + tag search, per-tile actions, bulk Rotate / Resize / Trash / Materialize on the filtered set.
  • Non-destructive edits (rotate, resize) with a per-user Undo/Redo buffer.
  • Trash with restore + permanent purge.
  • On-demand gallery.zip of the filtered set.
  • Optional AI tag suggestion via the Infomaniak chat completions API (per-user credentials editable on /me).

⁠Quick start

docker run -d \
  --name gallery-me \
  -p 8000:8000 \
  -v $(pwd)/data:/data \
  -e SECRET_KEY="$(openssl rand -hex 32)" \
  -e MAGIC_LINK_BASE_URL=http://localhost:8000 \
  -e SMTP_HOST=smtp.example.com \
  -e SMTP_PORT=587 \
  -e [email protected] \
  -e SMTP_PASSWORD=app-password \
  -e [email protected] \
  -e EMAIL_CONSOLE=false \
  -e [email protected] \
  toopy/gallery-me:latest

Open http://localhost:8000/⁠, enter the same email, click the link the backend emails you, and you're in. Alembic migrations run on container start against the SQLite file under /data, so the first run just works.

For a docker-compose setup with sensible defaults (incl. a local SMTP catcher), see the stack in the repo⁠.

⁠Volumes

PathPurpose
/dataImage bytes + SQLite database + variant cache. Persist this one.

The image runs as uid 0 in this entrypoint; if you bind-mount ./data make sure it's writable.

⁠Environment

The container reads pydantic-settings (env vars, case-insensitive). Required vars have no sensible default and the app refuses to boot without them.

⁠Sessions / cookies
VariableDefaultPurpose
SECRET_KEY(dev placeholder — change!)Session signing key. Use openssl rand -hex 32.
COOKIE_SECUREfalseSet true when serving behind HTTPS.
CORS_ORIGINShttp://localhost:5173,…Comma-separated CORS allow-list (only matters cross-origin).
VariableDefaultPurpose
SMTP_HOSTlocalhostOutbound SMTP host.
SMTP_PORT1025Outbound SMTP port.
SMTP_USER / SMTP_PASSWORD(empty)Auth credentials.
SMTP_FROM[email protected]From address used for magic-link emails.
EMAIL_CONSOLEtrueDev fallback: also print the magic link to stdout. Set false in prod.
MAGIC_LINK_BASE_URLhttp://localhost:8080Origin used in the email link — must reach the running container.
MAGIC_LINK_TTL_SECONDS900TTL of the magic link.
GALLERY_ACCOUNT_EMAIL(empty)If set, only this address can request a magic link. Everyone else is silently dropped.
VariableDefaultPurpose
DATABASE_URLsqlite+aiosqlite:////data/gallery_me.sqlite3DB connection string.
GALLERY_DATA_DIR/dataRoot for images / archives / cache.
GALLERY_CACHE_MAX_BYTES2147483648 (2 GiB)LRU eviction cap for the variant cache.
GALLERY_ALLOWED_WIDTHS160,320,480,800,1200,1600,2400Whitelisted resize widths.
GALLERY_ALLOWED_FORMATSjpeg,png,webpWhitelisted explicit transcode targets.
GALLERY_MAX_UPLOAD_BYTES52428800 (50 MiB)Per-file upload cap.
GALLERY_UNDO_BUFFER_SIZE100Per-user cap on rotate/resize undo entries.
⁠AI tagging (optional, Infomaniak)
VariableDefaultPurpose
INFOMANIAK_API_KEY(empty)Env-level fallback. Per-user keys on /me take precedence.
INFOMANIAK_PRODUCT_ID(empty)Env-level fallback.
INFOMANIAK_CHAT_MODELgoogle/gemma-4-31B-itDefault vision model identifier.
INFOMANIAK_MAX_TAGS_PER_IMAGE8Cap on the tag list per image.
INFOMANIAK_MAX_IMAGE_EDGE_PX1024Pillow downsample target before sending to the model.
INFOMANIAK_TIMEOUT_SECONDS30Per-request httpx timeout.

With both INFOMANIAK_API_KEY and INFOMANIAK_PRODUCT_ID empty the in-process tagging runtime loads NoOpChatClient — the rest of the app boots and works fine, only tag suggestion is disabled.

⁠Tags

TagBuilt from
toopy/gallery-me:latestmain branch pipelines.
toopy/gallery-me:<short-sha>Feature-branch pipelines (8-char commit SHA).

The GitLab pipeline that publishes this image lives at /.gitlab-ci.yml⁠ (publish:backend job).

⁠License + source

Source: https://gitlab.com/toopy/gallery-me⁠. Issues and merge requests welcome.

Tag summary

Content type

Image

Digest

sha256:ca38d7eea…

Size

86.5 MB

Last updated

5 months ago

docker pull toopy/gallery-me