- Python 66.8%
- JavaScript 12.2%
- HTML 10.9%
- CSS 9.2%
- Dockerfile 0.7%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| app | ||
| deploy | ||
| .dockerignore | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| README.md | ||
frames of entropy — галерея фотографа
Flask + PostgreSQL + PhotoSwipe. Фотографии, папки, тексты страниц и палитра сайта редактируются в админке; в файловой системе лежат только сами изображения.
Локальный запуск
cd app
python3 -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements-dev.txt
# нужна работающая PostgreSQL (или задайте свои POSTGRES_*)
export POSTGRES_PASSWORD=...
export ADMIN_PASSWORD=...
export SECRET_KEY=$(openssl rand -hex 32)
flask --app app db-bootstrap # накатывает миграции
python app.py # http://127.0.0.1:5000, админка — /admin
Без ADMIN_PASSWORD админка закрыта полностью — приложение предупреждает об этом в лог
при старте.
Тесты
cd app && venv/bin/python -m pytest
Тесты поднимают отдельный экземпляр приложения на SQLite в памяти и временных каталогах — ни базы, ни Docker не требуется.
Как устроен сайт
Публичная часть. Главная — горизонтальный drill-down по папкам: карточка папки с
четырьмя обложками, клик — уезд на уровень вглубь. Адрес отражает путь (/cars/sport),
поэтому ссылка на конкретную папку и кнопка «назад» в браузере работают. Ниже папок —
лента фото самой папки и всех вложенных, догружается порциями при прокрутке.
Что видно кому. У папки и у страницы есть два независимых флага:
| Флаг | Эффект |
|---|---|
| «Скрыт» | папка и всё внутри неё исчезают из дерева на главной |
| «Доступ по временной ссылке» | генерирует /album/<token> (для страниц — /page-link/<token>), который работает вне зависимости от флага «скрыт» |
Админка (/admin, HTTP basic auth):
- Фото (папки) — основной экран: дерево папок, drag-n-drop загрузка с описаниями, перетаскивание для ручного порядка, сортировка по имени/дате, флаги видимости.
- Фото (таблица) — плоский список для точечных правок.
- Настройки сайта — тексты шапки/подвала, палитра, идентификатор Google Analytics.
- Страницы — подстраницы в Markdown, попадают в меню автоматически.
- Логи — хвост текущего лога и скачивание архивов после ротации.
Изображения
Оригинал сохраняется под uuid-именем в static/photos/ — исходное имя остаётся только
как подпись (alt). Миниатюра (до 600 px по большей стороне, JPEG) создаётся при
загрузке и лениво восстанавливается при запросе /thumbs/<файл>, если её нет: том с
миниатюрами не персистентный, и после пересоздания контейнера они отстраиваются заново
по мере обращений. Прогреть их заранее — flask --app app rebuild-thumbs.
CLI
| Команда | Назначение |
|---|---|
flask --app app db-bootstrap |
накатывает миграции; умеет «усыновить» старую БД без Alembic-истории. Идемпотентна, гоняется при каждом старте контейнера |
flask --app app import-photos |
заносит в БД файлы, уже лежащие в static/photos/ (подпапка = папка-категория) |
flask --app app rebuild-thumbs |
прогревает миниатюры для всех фото |
Деплой
Push в master запускает .forgejo/workflows/deploy.yml: код синкается в
/opt/photo-gallery-src/src, секреты пишутся в /opt/photo-gallery-src/deploy/secrets,
далее docker compose up -d --build. Код и постоянные данные лежат в разных
директориях, чтобы синк никогда не задел секреты и тома.
Стек — два контейнера (app на gunicorn + postgres:17-alpine), наружу торчит только
127.0.0.1:8001, перед ним nginx. Пароли передаются docker-секретами (файлами), не через
environment. Подробности — в комментариях deploy/docker-compose.yml.
Внутреннее устройство кода описано в ARCHITECTURE.md.