Files
Andrey 41a4b0dd84 ✨ feat: запуск без .env — всё настраивается в UI, и порталы по дизайну
Первичное требование: поднял докер — прошёл мастер — дальше всё в интерфейсе.
Ни одной переменной окружения задавать не нужно и негде.

Запуск и секреты:
- .env.example удалён, мёртвые переменные вычищены; секреты инстанса
  генерирует одноразовый сервис secrets в именованный том, пароли БД —
  через POSTGRES_*_PASSWORD_FILE;
- Caddy: catch-all :80 и on_demand TLS вместо хостов в конфиге — свежая
  коробка отвечает по IP и по любому домену, до мастера дойти можно;
- адрес установки, SMTP и TURN переехали в настройки (InstanceSettings,
  миграции 0027–0030), внешние ссылки строятся от него;
- deploy/cli больше не читает .env; сборка образов в ghcr через GitHub
  Actions, release.env с digest-пинами;
- куки Secure/__Host- выставляются по факту TLS запроса, а не настройкой.

Порталы (дизайн-базлайн v2, кадры PT1–PT8):
- список, карточка портала, библиотека материалов, редактор статьи и
  настройки — ширины и ритм как в остальных разделах;
- файлы статьи: изображение вставляется в текст своим механизмом, файл
  прикрепляется вложением и выводится на портале списком с иконкой формата;
- ссылки на файлы приводятся к относительным: абсолютный хост резал CSP
  портала и картинка не появлялась;
- «Опубликовать» сверяется с сервером и публикует то, что на экране, а не
  ранее выбранную редакцию;
- колонка «Оценки» в списке статей и блок оценок в редакторе.

Первый запуск: полоса «демо-данные устанавливаются» — установка идёт в
worker, и без неё человек видел пустые разделы без объяснения.

Починен фон: вторичное хранилище стало best-effort — при живых остаточных
S3-ключах повторная загрузка файла падала в 500.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 20:37:48 +03:00

58 lines
2.5 KiB
Python

# ruff: noqa: F403,F405
import os
from chatballs_backend.settings_base import *
CHATBALLS_RUNTIME_SURFACE = "platform"
ROOT_URLCONF = "chatballs_backend.urls_platform"
ASGI_APPLICATION = "chatballs_backend.asgi_platform.application"
WSGI_APPLICATION = "chatballs_backend.wsgi_platform.application"
# Домены установки задаются в UI (мастер первого запуска), а не переменной
# окружения: коробка поднимается одной командой и до настройки отвечает на
# локальные адреса. Проверку хоста несёт middleware/список ниже.
ALLOWED_HOSTS = env_list(
"CHATBALLS_PLATFORM_ALLOWED_HOSTS",
["localhost", "127.0.0.1", "platform.localhost"],
)
if "backend-platform" not in ALLOWED_HOSTS:
ALLOWED_HOSTS.append("backend-platform")
CSRF_TRUSTED_ORIGINS = env_list("CHATBALLS_PLATFORM_CSRF_TRUSTED_ORIGINS", [])
CORS_ALLOWED_ORIGINS = []
# Caddy спрашивает разрешение на on-demand сертификат Help Center по plain HTTP
# из внутренней сети: заголовок X-Forwarded-Proto он не шлёт и по редиректам не
# ходит. Без исключения SECURE_SSL_REDIRECT отвечает 301, Caddy считает домен
# неавторизованным и сертификат портала не выпускается.
SECURE_REDIRECT_EXEMPT = [r"^api/v1/gateway/"]
SESSION_COOKIE_NAME = os.environ.get(
"CHATBALLS_PLATFORM_SESSION_COOKIE_NAME",
"__Host-chatballs-platform-session"
if SESSION_COOKIE_SECURE
else "chatballs_platform_session",
)
CSRF_COOKIE_NAME = os.environ.get(
"CHATBALLS_PLATFORM_CSRF_COOKIE_NAME",
"__Host-chatballs-platform-csrf"
if CSRF_COOKIE_SECURE
else "chatballs_platform_csrftoken",
)
# Имя cookie по http и его защищённая пара для запросов по TLS.
# Переключает TlsAwareCookieMiddleware по факту протокола запроса — тем же
# правилом, что и фронтенд (api/client.ts).
CHATBALLS_TLS_COOKIE_NAMES = {
SESSION_COOKIE_NAME: "__Host-chatballs-platform-session",
CSRF_COOKIE_NAME: "__Host-chatballs-platform-csrf",
}
SESSION_COOKIE_DOMAIN = None
CSRF_COOKIE_DOMAIN = None
SESSION_COOKIE_PATH = "/"
CSRF_COOKIE_PATH = "/"
CHATBALLS_CONTENT_SECURITY_POLICY = os.environ.get(
"CHATBALLS_PLATFORM_CSP",
"default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'",
)