Compare commits
191 Commits
v0.3.1
..
5627a23d17
| Author | SHA1 | Date | |
|---|---|---|---|
| 5627a23d17 | |||
| 2dfa3595d4 | |||
| 82ea5a738f | |||
| 1d963d49b1 | |||
| b5f21b028b | |||
| 901667789d | |||
| 0cded05137 | |||
| 8b8ce6a419 | |||
| b89e7dd495 | |||
| 829362c55f | |||
| b56fbef9a9 | |||
| 4e744c5d8a | |||
| 08ba2f5b75 | |||
| 4e0647b7e0 | |||
| a6587edb62 | |||
| b1feb33ac1 | |||
| 69527189f4 | |||
| df97daa836 | |||
| 6f97287077 | |||
| 4f1fcf7e60 | |||
| a1a9ae24da | |||
| a1869decd8 | |||
| 982d8e570c | |||
| a89be1a28d | |||
| 543babe82e | |||
| 70b2d930f4 | |||
| 5609041919 | |||
| afc77fbd04 | |||
| 58590e81ff | |||
| a9002ecf19 | |||
| 7b58f77013 | |||
| 85bc89d211 | |||
| 420e949515 | |||
| 14b9114fff | |||
| 080aa5dcfb | |||
| 80ff9dffd1 | |||
| 73065e4178 | |||
| f8f4c72293 | |||
| 4af0458a35 | |||
| cd51decae6 | |||
| 56a4ca2308 | |||
| 1fddbe642d | |||
| 7d2630d6c5 | |||
| aae71de356 | |||
| 2456eab93c | |||
| b34a73747b | |||
| b940e2e71e | |||
| 8928aa3feb | |||
| c0fed9c2a7 | |||
| f2d9b65825 | |||
| 07976769a7 | |||
| abb05b7330 | |||
| 452eed8a2e | |||
| a3b491516a | |||
| 84f57b53f3 | |||
| 83085740e0 | |||
| 3c33b3a90a | |||
| 4cff1cb33e | |||
| 978a3ad02e | |||
| b3aa0ce3b3 | |||
| 18ab1ec291 | |||
| 0bc88869a5 | |||
| ac8b50e3be | |||
| 683513e5fa | |||
| b8a6a2370e | |||
| 26d957a285 | |||
| fb7096ca3d | |||
| 9fb278368f | |||
| 9155609c88 | |||
| e9173e269a | |||
| b583afa080 | |||
| 4de35904f4 | |||
| 778b7f63dd | |||
| 61f69c21f7 | |||
| a84e780710 | |||
| 9097414637 | |||
| 2d830aa897 | |||
| 45681827e1 | |||
| 3479b31f0e | |||
| 21501799ca | |||
| 1660e76810 | |||
| c37a3ab097 | |||
| 47049f6f9f | |||
| 74220ab3a3 | |||
| 509bce5111 | |||
| 4bb77a9892 | |||
| c184d65c66 | |||
| 1bdff6589e | |||
| 34ab71172c | |||
| 851babdda2 | |||
| 330737878e | |||
| bc3c348560 | |||
| 0d97dafe3e | |||
| 1f0aaa7687 | |||
| 20877dc98e | |||
| 19a595fb10 | |||
| 917d62cc21 | |||
| 71b5089a61 | |||
| 9e27089638 | |||
| af67b69418 | |||
| af2489f923 | |||
| 0b6536b8f4 | |||
| c930e059a0 | |||
| c0b41cd866 | |||
| b348c8fe81 | |||
| e7da2cdeb9 | |||
| d5719015b0 | |||
| bcc10de4d4 | |||
| 55f9a74231 | |||
| 49c19d9f92 | |||
| 67a8c093f5 | |||
| e0a5a5739b | |||
| 3e67d6cf71 | |||
| 917bcb42c8 | |||
| 5bdb97f5e8 | |||
| e2091b72b1 | |||
| 446cbe8c5c | |||
| bdca8dfc6a | |||
| 111dd7bd33 | |||
| 57a522f4b7 | |||
| 2b37f709a9 | |||
| f480dc3666 | |||
| 2da036e4e0 | |||
| 28620ce2a9 | |||
| 574ae84321 | |||
| 796cb89525 | |||
| 0e8839bf18 | |||
| dbd622e456 | |||
| 741dd2685e | |||
| c01873144e | |||
| 8387d36abf | |||
| 2bc0869104 | |||
| 8c7972178c | |||
| c338a17dd8 | |||
| 3cc4ae39f7 | |||
| d789b2443a | |||
| 25beecdf60 | |||
| 5eb2f304a3 | |||
| 0246c1de8c | |||
| 4196cd64d8 | |||
| 60b6582f1b | |||
| 4a91928bc7 | |||
| b322331366 | |||
| e041110059 | |||
| ae1b851db7 | |||
| 28aa35d617 | |||
| b296ad79e9 | |||
| fbea58b1c4 | |||
| 30084dd6ac | |||
| c961b64036 | |||
| ad74f0a51e | |||
| 78ec6077b6 | |||
| c68fa5be07 | |||
| ee1dd31d54 | |||
| 6066e5df29 | |||
| 8704f4125b | |||
| 2777f3fde2 | |||
| 4b9ca319f9 | |||
| 56d7af5c9d | |||
| 0368c1c34e | |||
| fbdf95b66f | |||
| 8fdaf6935e | |||
| 8272bdb25f | |||
| 6a4c315cdc | |||
| f2c89abe1c | |||
| 37a605ef09 | |||
| 28a680fdf1 | |||
| 9bcbc73602 | |||
| 8ca1f5b36e | |||
| 6c4dcd1cb7 | |||
| 5a38181cd3 | |||
| 5738ee9c9e | |||
| 9535424e6a | |||
| 4e03fead67 | |||
| 5de28c8bfb | |||
| de45931ff4 | |||
| d27d0e7173 | |||
| f3a8a65afe | |||
| 72b71c7810 | |||
| 6bd8143d83 | |||
| a970b7109b | |||
| 99b8ba80d5 | |||
| 7cc438d1c5 | |||
| 034ec20c26 | |||
| 90ddc0d88c | |||
| 6e92eca67a | |||
| 6a9c0e4568 | |||
| 265732bae8 | |||
| cded4e407e | |||
| f996dd891e | |||
| 748a1882e5 |
@@ -152,14 +152,6 @@ ALLOW_MEDIA_SERVE=False
|
|||||||
# - AWS Systems Manager Parameter Store
|
# - AWS Systems Manager Parameter Store
|
||||||
# - HashiCorp Vault
|
# - HashiCorp Vault
|
||||||
|
|
||||||
|
|
||||||
# ****************************************************************************************************************
|
|
||||||
# Системные пути на хосте (ТОЛЬКО ДЛЯ ПРОДАКШЕНА)
|
|
||||||
# Используется скриптом для генерации корректного Nginx конфига (alias к медиа-файлам).
|
|
||||||
# На локальной машине разработчика (Dev) эта переменная игнорируется.
|
|
||||||
# В ПРОДАКШЕНЕ: Укажите полный путь к папке проекта на сервере
|
|
||||||
HOST_PROJECT_PATH=/home/username/projects
|
|
||||||
|
|
||||||
# ****************************************************************************************************************
|
# ****************************************************************************************************************
|
||||||
# Настройки доступа к пакетам в репозитории, чтобы wathtower мог проверять их свежесть и скачивать для обновления.
|
# Настройки доступа к пакетам в репозитории, чтобы wathtower мог проверять их свежесть и скачивать для обновления.
|
||||||
# Получить эти данные можно в настройках вашего репозитория, например:
|
# Получить эти данные можно в настройках вашего репозитория, например:
|
||||||
|
|||||||
@@ -1,242 +0,0 @@
|
|||||||
# Production Deployment Guide для Oknardia
|
|
||||||
|
|
||||||
## Структура на хосте
|
|
||||||
|
|
||||||
Структура директорий на production сервере должна быть:
|
|
||||||
|
|
||||||
```
|
|
||||||
~/docker-app/oknardia-site/
|
|
||||||
├── .env # Переменные окружения Django
|
|
||||||
├── config/ # Конфиги
|
|
||||||
│ └── nginx/
|
|
||||||
│ └── oknardia-app--external-nginx.conf # Конфиг Nginx (шаблон)
|
|
||||||
├── database/ # БД SQLite (создается автоматически)
|
|
||||||
│ └── oknardia.sqlite3
|
|
||||||
├── media/ # Медиа файлы, uploads, кеши
|
|
||||||
│ ├── img_avatar/
|
|
||||||
│ ├── img_seria/
|
|
||||||
│ ├── _serv_sitemap/
|
|
||||||
│ └── _error/ # Error pages (генерируются контейнером)
|
|
||||||
└── docker-compose.prod.yml # Конфиг Docker
|
|
||||||
```
|
|
||||||
|
|
||||||
## Установка на Production сервере
|
|
||||||
|
|
||||||
### 1. Создать директории и скопировать файлы
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# На хосте
|
|
||||||
mkdir -p ~/docker-app/oknardia-site/{config/nginx,database,media}
|
|
||||||
|
|
||||||
# Скопировать docker-compose.prod.yml и .env из проекта
|
|
||||||
cp docker-compose.prod.yml ~/docker-app/oknardia-site/
|
|
||||||
cp .env ~/docker-app/oknardia-site/
|
|
||||||
|
|
||||||
# Скопировать конфиг nginx
|
|
||||||
cp config/nginx/oknardia-app--external-nginx.conf ~/docker-app/oknardia-site/config/nginx/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Настроить .env на сервере
|
|
||||||
|
|
||||||
Отредактировать `~/docker-app/oknardia-site/.env`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ОБЯЗАТЕЛЬНО заполнить эти переменные!
|
|
||||||
|
|
||||||
# Путь к проекту на хосте (используется для sed-замены в конфиге nginx)
|
|
||||||
HOST_PROJECT_PATH=/home/default_user/projects/oknardia-site
|
|
||||||
|
|
||||||
# Credentials для скачивания образа из реестра Gitea
|
|
||||||
REPO_USER=имя_пользователя_gitea
|
|
||||||
REPO_PASS=токен_из_личного_кабинета_gitea
|
|
||||||
|
|
||||||
# Остальное скопировать из локального .env
|
|
||||||
DEBUG=False
|
|
||||||
DJANGO_SECRET_KEY=...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Важно:** `HOST_PROJECT_PATH` должен совпадать с корневой папкой проекта на хосте, куда ты скопировал docker-compose.prod.yml!
|
|
||||||
|
|
||||||
### 3. Запустить контейнеры
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/docker-app/oknardia-site/
|
|
||||||
|
|
||||||
# Первый запуск (холодный старт)
|
|
||||||
docker compose -f docker-compose.prod.yml up -d
|
|
||||||
|
|
||||||
# Проверить логи
|
|
||||||
docker compose -f docker-compose.prod.yml logs -f web
|
|
||||||
|
|
||||||
# Проверить статус контейнеров
|
|
||||||
docker compose -f docker-compose.prod.yml ps
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Проверить что nginx конфиг был сгенерирован
|
|
||||||
|
|
||||||
После первого запуска контейнер должен:
|
|
||||||
- Создать `/config/nginx/oknardia-app--external-nginx.conf` (боевой конфиг)
|
|
||||||
- Создать `/config/nginx/oknardia-app--external-nginx.conf.example` (exemplo)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ls -la ~/docker-app/oknardia-site/config/nginx/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Настроить Nginx на хосте
|
|
||||||
|
|
||||||
На хосте установить Nginx и скопировать/ссылаться на конфиг из контейнера:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# На хосте (например, Ubuntu 20.04)
|
|
||||||
sudo systemctl install nginx
|
|
||||||
|
|
||||||
# Включить сгенерированный конфиг в основной конфиг Nginx
|
|
||||||
# /etc/nginx/sites-available/oknardia или /etc/nginx/conf.d/oknardia.conf
|
|
||||||
|
|
||||||
# Пример:
|
|
||||||
sudo ln -s /home/default_user/projects/oknardia-site/config/nginx/oknardia-app--external-nginx.conf \
|
|
||||||
/etc/nginx/sites-enabled/oknardia
|
|
||||||
|
|
||||||
# Проверить и перезагрузить Nginx
|
|
||||||
sudo nginx -t
|
|
||||||
sudo systemctl reload nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
## Команды для управления
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/docker-app/oknardia-site/
|
|
||||||
|
|
||||||
# Запустить контейнеры (если они были остановлены)
|
|
||||||
docker compose -f docker-compose.prod.yml up -d
|
|
||||||
|
|
||||||
# Остановить контейнеры
|
|
||||||
docker compose -f docker-compose.prod.yml down
|
|
||||||
|
|
||||||
# Перезагрузить контейнеры (удалить и создать заново)
|
|
||||||
docker compose -f docker-compose.prod.yml down
|
|
||||||
docker compose -f docker-compose.prod.yml up -d
|
|
||||||
|
|
||||||
# Посмотреть логи
|
|
||||||
docker compose -f docker-compose.prod.yml logs -f web
|
|
||||||
docker compose -f docker-compose.prod.yml logs -f watchtower
|
|
||||||
|
|
||||||
# Выполнить команду внутри контейнера Django
|
|
||||||
docker compose -f docker-compose.prod.yml exec web python manage.py shell
|
|
||||||
|
|
||||||
# Пересоздать пре-рендер кеши вручную
|
|
||||||
docker compose -f docker-compose.prod.yml exec web python manage.py regenerate_seria_prerender
|
|
||||||
```
|
|
||||||
|
|
||||||
## Что делает docker-compose.prod.yml
|
|
||||||
|
|
||||||
### Сервис `web` (Django + Gunicorn)
|
|
||||||
|
|
||||||
При старте контейнера:
|
|
||||||
1. ✅ Применяет миграции БД (`migrate --noinput`)
|
|
||||||
2. ✅ Собирает статику (`collectstatic --noinput`)
|
|
||||||
3. ✅ Генерирует sitemap'ы (`generate_sitemaps`)
|
|
||||||
4. ✅ Пересоздает пре-рендер кеши для серий (`regenerate_seria_prerender`)
|
|
||||||
5. ✅ Пересчитывает рейтинги (`make_rating`)
|
|
||||||
6. ✅ Копирует конфиг nginx с авто-заменой путей (через `sed`)
|
|
||||||
7. ✅ Запускает Gunicorn на `127.0.0.1:8000`
|
|
||||||
|
|
||||||
### Сервис `watchtower` (авто-обновление)
|
|
||||||
|
|
||||||
- 👁️ Следит за реестром Gitea (проверка каждые 30 минут)
|
|
||||||
- 🔄 Автоматически скачивает новый образ, если он есть
|
|
||||||
- ♻️ По-graceful перезагружает контейнер web
|
|
||||||
- 🗑️ Удаляет старые образы (WATCHTOWER_CLEANUP=true)
|
|
||||||
|
|
||||||
Watchtower использует метку на контейнере `web`:
|
|
||||||
```yaml
|
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.scope=oknardia-scope"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Файлы, которые монтируются с хоста
|
|
||||||
|
|
||||||
| На хосте | В контейнере | Назначение |
|
|
||||||
|----------|----------|-----------|
|
|
||||||
| `./database/` | `/home/app/database/` | БД SQLite (КРИТИЧНО!) |
|
|
||||||
| `./media/` | `/home/app/public/media/` | Медиа файлы, uploads |
|
|
||||||
| `./config/` | `/nginx_configs_host/` | Конифиги (читаются и пишущутся контейнером) |
|
|
||||||
|
|
||||||
## Переменные окружения в docker-compose.prod.yml
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
- DEBUG=False # Production: без debug
|
|
||||||
- DJANGO_LOG_LEVEL=INFO # Минимум логов
|
|
||||||
- ALLOW_MEDIA_SERVE=False # Nginx обслуживает медиа
|
|
||||||
- HOST_PROJECT_PATH=... # Путь для sed-замены в конфиге nginx
|
|
||||||
- REPO_USER=${REPO_USER} # Из .env (для Watchtower)
|
|
||||||
- REPO_PASS=${REPO_PASS} # Из .env (для Watchtower)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Health Check
|
|
||||||
|
|
||||||
Контейнер имеет healthcheck, который проверяет доступность Django:
|
|
||||||
- Проверка каждые 3 минуты
|
|
||||||
- Таймаут: 12 секунд
|
|
||||||
- Unhealthy после 3 неудачных попыток
|
|
||||||
- Это критично для Watchtower (он ждет что контейнер станет healthy перед finalization)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### ContainerError при запуске
|
|
||||||
|
|
||||||
Проверить логи:
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yml logs web
|
|
||||||
```
|
|
||||||
|
|
||||||
### БД не синхронизируется между перезагрузками
|
|
||||||
|
|
||||||
Убедиться что `./database/` правильно монтирует:
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yml exec web ls -la /home/app/database/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Watchtower не обновляет контейнер
|
|
||||||
|
|
||||||
Проверить:
|
|
||||||
- Есть ли интернет на сервере
|
|
||||||
- Правильны ли REPO_USER и REPO_PASS в .env
|
|
||||||
- Существует ли образ `oknardia:latest` в реестре
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yml logs watchtower
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx конфиг не обновился
|
|
||||||
|
|
||||||
Конфиг копируется при старте контейнера. Если нужно пересоздать:
|
|
||||||
```bash
|
|
||||||
docker compose -f docker-compose.prod.yml down
|
|
||||||
docker compose -f docker-compose.prod.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
Потом проверить:
|
|
||||||
```bash
|
|
||||||
cat ~/docker-app/oknardia-site/config/nginx/oknardia-app--external-nginx.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
## Резервные копии
|
|
||||||
|
|
||||||
Важно регулярно бэкапить:
|
|
||||||
- `./database/oknardia.sqlite3` - БД
|
|
||||||
- `./media/` - Загруженные файлы
|
|
||||||
|
|
||||||
Пример cronjob для ежедневного бэкапа:
|
|
||||||
```bash
|
|
||||||
# /etc/cron.daily/backup-oknardia
|
|
||||||
#!/bin/bash
|
|
||||||
BACKUP_DIR="/backup/oknardia"
|
|
||||||
APP_DIR="/home/default_user/projects/oknardia-site"
|
|
||||||
|
|
||||||
mkdir -p $BACKUP_DIR
|
|
||||||
tar -czf $BACKUP_DIR/oknardia-$(date +%Y%m%d).tar.gz $APP_DIR
|
|
||||||
find $BACKUP_DIR -name "oknardia-*.tar.gz" -mtime +30 -delete # Удалять старше 30 дней
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,20 +1,27 @@
|
|||||||
# config/nginx/oknardia-app--external-nginx.conf
|
# config/nginx/oknardia-app--external-nginx.conf
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# КОНФИГУРАЦИОННЫЙ ФАЙЛ NGINX (Reverse Proxy для Docker + Production)
|
# ЭТАЛОННЫЙ КОНФИГУРАЦИОННЫЙ ФАЙЛ NGINX (Reverse Proxy для Docker)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
#
|
#
|
||||||
# ИНФОРМАЦИЯ:
|
# ВНИМАНИЕ:
|
||||||
# 1. Этот файл используется как шаблон при деплое
|
# Этот файл является шаблоном. При первом деплое он копируется
|
||||||
# 2. При первом деплое пути `/home/user/path-to-oknardia-app` заменяются на реальный путь через sed
|
# в `/home/user/path-to-oknardia-app/config/nginx/oknardia-app--external-nginx.conf`,
|
||||||
# 3. Сгенерированный конфиг скопируется в `/etc/nginx/sites-available/oknardia`
|
# а затем (уже на хосте, руками) через силинк в `/etc/nginx/sites-available/` и активируется.
|
||||||
# 4. Последующие деплои ОБНОВЛЯЮТ этот файл автоматически (sed + копирование)
|
# При последующих деплоях `oknardia-app--external-nginx.conf` НЕ ПЕРЕЗАПИСЫВАЕТСЯ,
|
||||||
|
# чтобы не затереть SSL-сертификаты и ручные правки.
|
||||||
#
|
#
|
||||||
# АРХИТЕКТУРА:
|
# Если вы изменили этот файл в репозитории и хотите применить изменения на проде:
|
||||||
# - Nginx (порты 80/443) <-> Gunicorn контейнер (localhost:8000)
|
# вам нужно обновить файл в `/home/user/path-to-oknardia-app/config/nginx/oknardia-app--external-nginx.conf` вручную (diff + copy).
|
||||||
# - Медиа файлы отдаются из `/home/user/path-to-oknardia-app/media/` напрямую
|
#
|
||||||
# - Ошибки 5xx берутся из `media/_error` (копируются контейнером при старте)
|
# Так же (рядом) будет создан образец этого файла `nginx_oknardia.conf.example`, который будет обновляться при деплоях
|
||||||
# - Sitemap.xml отдается через Django/WhiteNoise (в media/_serv_sitemap/)
|
# из репозитория, чтобы вы могли видеть, что изменилось и при необходимости перенести эти изменения на прод.
|
||||||
# - Static файлы (/static/) тоже отдаются через Django/WhiteNoise
|
#
|
||||||
|
# Предполагаемая структура на сервере:
|
||||||
|
# /home/user/path-to-oknardia-app/
|
||||||
|
# ├── docker-compose.yml
|
||||||
|
# ├── .env
|
||||||
|
# ├── media/ <-- Сюда Nginx смотрит напрямую (Docker volume)
|
||||||
|
# └── ...
|
||||||
|
|
||||||
# 1. Описываем, где живет наш Django в Docker
|
# 1. Описываем, где живет наш Django в Docker
|
||||||
upstream oknardia-django {
|
upstream oknardia-django {
|
||||||
@@ -63,28 +70,22 @@ server {
|
|||||||
|
|
||||||
# --- МЕДИА ФАЙЛЫ (Загруженный контент) ---
|
# --- МЕДИА ФАЙЛЫ (Загруженный контент) ---
|
||||||
# Nginx отдает их напрямую с диска хоста, не дергая Docker.
|
# Nginx отдает их напрямую с диска хоста, не дергая Docker.
|
||||||
# Сюда входит: загруженные картинки, документы, свитмапы, ошибки 5xx
|
|
||||||
# Путь должен совпадать с тем, где лежит volume на хост-машине.
|
# Путь должен совпадать с тем, где лежит volume на хост-машине.
|
||||||
|
# ВАЖНО: Убедитесь, что пользователь nginx (www-data) имеет права на чтение этой папки!
|
||||||
|
# ТРЕБУЕТСЯ ЗАМЕНА ПРИ ДЕПЛОЕ: /home/user/path-to-oknardia-app -> ваш реальный путь
|
||||||
location /media/ {
|
location /media/ {
|
||||||
alias /home/user/path-to-oknardia-app/media/;
|
alias /home/user/path-to-oknardia-app/media/;
|
||||||
expires 30d; # Кешируем картинки на месяц
|
expires 30d; # Кешируем картинки на месяц
|
||||||
add_header Cache-Control "public, no-transform";
|
add_header Cache-Control "public, no-transform";
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- SITEMAP.XML ---
|
|
||||||
# Sitemap хранится в media/_serv_sitemap/ но должен быть доступен из корня
|
|
||||||
# для поисковиков (они ищут http://example.com/sitemap.xml)
|
|
||||||
location = /sitemap.xml {
|
|
||||||
alias /home/user/path-to-oknardia-app/media/_serv_sitemap/sitemap.xml;
|
|
||||||
expires 7d; # Кешируем на неделю (редко меняется)
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- СТРАНИЦЫ ОШИБОК (Custom Error Pages) ---
|
# --- СТРАНИЦЫ ОШИБОК (Custom Error Pages) ---
|
||||||
# Если Django упал (502) или сработал тайм-аут (504), Nginx должен отдать статический HTML.
|
# Если Django упал (502) или сработал тайм-аут (504), Nginx должен отдать статический HTML.
|
||||||
# Эти файлы копируются в `media/_error` при старте контейнера Docker.
|
# Эти файлы должны лежать в папке, доступной Nginx (например, в `media/_error`).
|
||||||
#
|
#
|
||||||
# ВАЖНО: error_page директива перехватывает ошибки от апстрима (Gunicorn).
|
# ВАЖНО:
|
||||||
|
# 1. Файлы 50x.html (500, 502, 503, 504) копируются в `media/_error` при старте контейнера (см. docker-compose.prod.yml -> command).
|
||||||
|
# 2. error_page директива перехватывает ошибки от апстрима (Gunicorn).
|
||||||
error_page 500 /500.html;
|
error_page 500 /500.html;
|
||||||
error_page 502 /502.html;
|
error_page 502 /502.html;
|
||||||
error_page 503 /503.html;
|
error_page 503 /503.html;
|
||||||
@@ -95,6 +96,8 @@ server {
|
|||||||
location = /503.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
location = /503.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
||||||
location = /504.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
location = /504.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
||||||
|
|
||||||
|
# 404 (и другие) тоже нужно кастомизировать... обычно Django сам отдает 404.
|
||||||
|
# Но, например, Nginx отдаст 404 при ошике доступа к media-файлам (они храняться на хосте, а не в контейнере)
|
||||||
error_page 400 /400.html;
|
error_page 400 /400.html;
|
||||||
error_page 401 /401.html;
|
error_page 401 /401.html;
|
||||||
error_page 403 /403.html;
|
error_page 403 /403.html;
|
||||||
@@ -109,6 +112,7 @@ server {
|
|||||||
location = /413.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
location = /413.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
||||||
location = /429.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
location = /429.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
||||||
|
|
||||||
|
# Указываем единую страницу (на реконструкции) для всех прочих ошибок
|
||||||
error_page 405 406 407 408 409 410 411 412 414 415 416 417 418 421 422 423 424 425 426 428 431 451 /under_reconstruction.html;
|
error_page 405 406 407 408 409 410 411 412 414 415 416 417 418 421 422 423 424 425 426 428 431 451 /under_reconstruction.html;
|
||||||
location = /under_reconstruction.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
location = /under_reconstruction.html { root /home/user/path-to-oknardia-app/media/_error; internal; }
|
||||||
|
|
||||||
@@ -137,11 +141,10 @@ server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# 3. Редирект с www на основной домен (SEO best practice)
|
# 3. Редирект с www на без-www (SEO best practice)
|
||||||
server {
|
server {
|
||||||
server_name www.tmp.oknardia.ru;
|
server_name www.tmp.oknardia.ru;
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
return 301 $scheme://oknardia.ru$request_uri; # Всегда редиректим на основной домен
|
||||||
return 301 $scheme://tmp.oknardia.ru$request_uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,17 +29,13 @@ services:
|
|||||||
# 3. Собираем статику
|
# 3. Собираем статику
|
||||||
# 4. Генерируем sitemap'ы
|
# 4. Генерируем sitemap'ы
|
||||||
# 5. Пересоздаём пре-рендер шаблоны серий
|
# 5. Пересоздаём пре-рендер шаблоны серий
|
||||||
# 6. Пересчитываем рейтинги профилей, стеклопакетов и наборов
|
# 6. Запускаем Gunicorn (нет live reload — это production режим!)
|
||||||
# (метод Манна-Уитни: может занять 30-60 сек на БД с 94+ профилями)
|
|
||||||
# 7. Запускаем Gunicorn (нет live reload — это production режим!)
|
|
||||||
# (timeout=120 сек для запуска, достаточно для всех процедур инициализации)
|
|
||||||
command: >
|
command: >
|
||||||
sh -c "mkdir -p /home/app/public/media &&
|
sh -c "mkdir -p /home/app/public/media &&
|
||||||
python manage.py migrate --noinput &&
|
python manage.py migrate --noinput &&
|
||||||
python manage.py collectstatic --noinput &&
|
python manage.py collectstatic --noinput &&
|
||||||
python manage.py generate_sitemaps &&
|
python manage.py generate_sitemaps &&
|
||||||
python manage.py regenerate_seria_prerender &&
|
python manage.py regenerate_seria_prerender &&
|
||||||
python manage.py make_rating &&
|
|
||||||
python -m gunicorn --workers 2 --bind 0.0.0.0:8000 --timeout 120 oknardia.wsgi:application"
|
python -m gunicorn --workers 2 --bind 0.0.0.0:8000 --timeout 120 oknardia.wsgi:application"
|
||||||
|
|
||||||
# 2. МОНТИРОВАНИЕ КОДА И ДАННЫХ
|
# 2. МОНТИРОВАНИЕ КОДА И ДАННЫХ
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
# ==============================================================================
|
|
||||||
# Docker Compose для PRODUCTION
|
|
||||||
# Этот файл запускается на боевом сервере.
|
|
||||||
# Вариант 1 (если переименовали в docker-compose.yml): docker compose up -d
|
|
||||||
# Вариант 2 (если оставили имя): docker compose -f docker-compose.prod.yml up -d
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
services:
|
|
||||||
# --- ОСНОВНОЙ СЕРВИС: DJANGO + GUNICORN + WHITENOISE ---
|
|
||||||
web:
|
|
||||||
# Имя контейнера для удобства
|
|
||||||
container_name: oknardia-backend
|
|
||||||
|
|
||||||
# В production используем готовый, собранный образ из реестра (Gitea)
|
|
||||||
image: git.cube2.ru/erjemin/oknardia:latest
|
|
||||||
|
|
||||||
# ПЕРЕЗАПУСК при сбое
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
# Метки для Watchtower (авто-обновление)
|
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.scope=oknardia-scope"
|
|
||||||
|
|
||||||
# КОМАНДА ЗАПУСКА (Production режим)
|
|
||||||
# При старте контейнера:
|
|
||||||
# 1. Применяем миграции
|
|
||||||
# 2. Собираем статику
|
|
||||||
# 3. Генерируем sitemap'ы
|
|
||||||
# 4. Пересоздаём пре-рендер шаблоны серий
|
|
||||||
# 5. Пересчитываем рейтинги
|
|
||||||
# 6. Создаём папку конфигов nginx (если нет)
|
|
||||||
# 7. Копируем конфиг nginx с авто-заменой путей через sed
|
|
||||||
# (переменная HOST_PROJECT_PATH берется из .env и подставляется в контейнер)
|
|
||||||
# 8. Инициализируем боевой конфиг (если нет, копируем из примера)
|
|
||||||
# 9. Запускаем Gunicorn
|
|
||||||
command: >
|
|
||||||
sh -c "python manage.py migrate --noinput &&
|
|
||||||
python manage.py collectstatic --noinput &&
|
|
||||||
python manage.py generate_sitemaps &&
|
|
||||||
python manage.py regenerate_seria_prerender &&
|
|
||||||
python manage.py make_rating &&
|
|
||||||
mkdir -p /nginx_configs_host/nginx &&
|
|
||||||
sed \"s|/home/user/path-to-oknardia-app|$${HOST_PROJECT_PATH}|g\" /home/app/config/nginx/oknardia-app--external-nginx.conf > /nginx_configs_host/nginx/oknardia-app--external-nginx.conf.example &&
|
|
||||||
if [ ! -f /nginx_configs_host/nginx/oknardia-app--external-nginx.conf ]; then
|
|
||||||
cp /nginx_configs_host/nginx/oknardia-app--external-nginx.conf.example /nginx_configs_host/nginx/oknardia-app--external-nginx.conf;
|
|
||||||
echo 'INIT: Created new nginx config with correct paths';
|
|
||||||
fi &&
|
|
||||||
ERROR_DIR=/home/app/web/public/media/_error &&
|
|
||||||
mkdir -p "$$ERROR_DIR" &&
|
|
||||||
for code in 400 401 403 404 413 429 500 502 503 504; do
|
|
||||||
cp /home/user/path-to-oknardia-app/oknardia/templates/error/$${code}.html "$$ERROR_DIR/$${code}.html";
|
|
||||||
done &&
|
|
||||||
cp /home/user/path-to-oknardia-app/oknardia/templates/error/under_reconstruction.html "$$ERROR_DIR/under_reconstruction.html"
|
|
||||||
python -m gunicorn --workers 2 --bind 0.0.0.0:8000 --timeout 120 oknardia.wsgi:application"
|
|
||||||
|
|
||||||
# Пробрасывание портов
|
|
||||||
# Слушаем только на localhost хоста для безопасности
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:8060:8000"
|
|
||||||
|
|
||||||
# МОНТИРОВАНИЕ ТОМОВ (Volumes)
|
|
||||||
volumes:
|
|
||||||
# БД SQLite
|
|
||||||
- ./database:/home/app/database
|
|
||||||
|
|
||||||
# Медиа файлы
|
|
||||||
- ./media:/home/app/public/media
|
|
||||||
|
|
||||||
# Конфиги nginx
|
|
||||||
- ./config:/nginx_configs_host
|
|
||||||
|
|
||||||
# Пользователь и права
|
|
||||||
user: "0:0"
|
|
||||||
|
|
||||||
# ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ (Production)
|
|
||||||
# .env файл содержит все sensitive данные (DB, Email, API keys, REPO_USER/PASS)
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
- DJANGO_SETTINGS_MODULE=oknardia.settings
|
|
||||||
- PYTHONUNBUFFERED=1
|
|
||||||
- DEBUG=False
|
|
||||||
- DJANGO_LOG_LEVEL=INFO
|
|
||||||
- ALLOW_MEDIA_SERVE=False
|
|
||||||
|
|
||||||
# ЗДОРОВЬЕ КОНТЕЙНЕРА (Healthcheck)
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()"]
|
|
||||||
interval: 3m
|
|
||||||
timeout: 12s
|
|
||||||
start_period: 60s
|
|
||||||
retries: 3
|
|
||||||
|
|
||||||
# ЛОГИРОВАНИЕ (Ротация)
|
|
||||||
logging:
|
|
||||||
driver: "json-file"
|
|
||||||
options:
|
|
||||||
max-size: "10m"
|
|
||||||
max-file: "5"
|
|
||||||
|
|
||||||
# РЕСУРСЫ
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: '0.75'
|
|
||||||
memory: 768M
|
|
||||||
mem_limit: 768M
|
|
||||||
|
|
||||||
# --- WATCHTOWER: АВТО-ОБНОВЛЕНИЕ ОБРАЗОВ ---
|
|
||||||
watchtower:
|
|
||||||
image: containrrr/watchtower
|
|
||||||
container_name: oknardia_watchtower
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
environment:
|
|
||||||
- REPO_USER=${REPO_USER}
|
|
||||||
- REPO_PASS=${REPO_PASS}
|
|
||||||
- WATCHTOWER_SCOPE=oknardia-scope
|
|
||||||
- WATCHTOWER_CLEANUP=true
|
|
||||||
- DOCKER_API_VERSION=1.44
|
|
||||||
- WATCHTOWER_WAIT_ON_TIMEOUT=60
|
|
||||||
- WATCHTOWER_LIFECYCLE_HOOKS=true
|
|
||||||
command: --interval 1800 --cleanup
|
|
||||||
logging:
|
|
||||||
driver: "json-file"
|
|
||||||
options:
|
|
||||||
max-size: "10m"
|
|
||||||
max-file: "3"
|
|
||||||
|
|
||||||
@@ -188,10 +188,7 @@ if database_engine == 'django.db.backends.sqlite3':
|
|||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': str(sqlite_db_path),
|
'NAME': str(sqlite_db_path),
|
||||||
'OPTIONS': {
|
}
|
||||||
'timeout': 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# База не SQLite (mariaDB, например): читаем все параметры подключения из env.
|
# База не SQLite (mariaDB, например): читаем все параметры подключения из env.
|
||||||
|
|||||||
+18
-93
@@ -1,13 +1,24 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""oknardia Конфигурация URL"""
|
"""oknardia Конфигурация URL
|
||||||
|
|
||||||
|
Список `urlpatterns` направляет URL-адреса в представления. Дополнительную информацию см.:
|
||||||
|
https://docs.djangoproject.com/en/4.1/topics/http/urls/
|
||||||
|
Примеры:
|
||||||
|
Представления функций
|
||||||
|
1. Добавьте import: из представлений импорта my_app
|
||||||
|
2. Добавьте URL-адрес в urlpatterns: path('', views.home, name='home')
|
||||||
|
Представления на основе классов
|
||||||
|
1. Добавьте импорт: from other_app.views import Home
|
||||||
|
2. Добавьте URL-адрес в шаблоны URL-адресов: path('', Home.as_view(), name='home')
|
||||||
|
Включение другой конфигурации URL
|
||||||
|
1. Импортируйте функцию include(): из django.urls import include, path
|
||||||
|
2. Добавьте URL-адрес в urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, re_path, path
|
from django.urls import include, path, re_path
|
||||||
from django.http import FileResponse
|
from django.conf.urls.static import static
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import environ
|
import environ
|
||||||
import mimetypes
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Инициализируем env
|
# Инициализируем env
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
@@ -16,67 +27,6 @@ from oknardia.settings import *
|
|||||||
from web import views, autocomplete_addr, user_manager, blog, diagrams, report1, report2, catalog, prices, service, \
|
from web import views, autocomplete_addr, user_manager, blog, diagrams, report1, report2, catalog, prices, service, \
|
||||||
catalog_profiles, catalog_series, catalog_openings, catalog_companies
|
catalog_profiles, catalog_series, catalog_openings, catalog_companies
|
||||||
|
|
||||||
|
|
||||||
def _serve_public_root_file(request, path):
|
|
||||||
"""Отдает статик-файл из public/ с правильным Content-Type и UTF-8 для текстовых файлов."""
|
|
||||||
file_path = PUBLIC_ROOT / path
|
|
||||||
|
|
||||||
# Безопасность: проверяем, что файл гарантированно внутри PUBLIC_ROOT
|
|
||||||
try:
|
|
||||||
file_path = file_path.resolve()
|
|
||||||
file_path.relative_to(PUBLIC_ROOT.resolve())
|
|
||||||
except ValueError:
|
|
||||||
from django.http import Http404
|
|
||||||
raise Http404("Файл не найден")
|
|
||||||
|
|
||||||
if not file_path.is_file():
|
|
||||||
from django.http import Http404
|
|
||||||
raise Http404("Файл не найден")
|
|
||||||
|
|
||||||
# Определяем MIME type
|
|
||||||
mime_type, _ = mimetypes.guess_type(str(file_path))
|
|
||||||
|
|
||||||
# Для текстовых файлов добавляем charset=utf-8
|
|
||||||
if mime_type and mime_type.startswith('text'):
|
|
||||||
mime_type = f"{mime_type}; charset=utf-8"
|
|
||||||
|
|
||||||
response = FileResponse(open(file_path, 'rb'), content_type=mime_type)
|
|
||||||
|
|
||||||
# Определяем Content-Disposition в зависимости от типа файла:
|
|
||||||
# inline — браузер отображает/обрабатывает (иконки, картинки, CSS, JS, robots.txt, manifest)
|
|
||||||
# attachment — браузер скачивает (архивы, документы и т.д.)
|
|
||||||
inline_types = {
|
|
||||||
'image/', # все картинки (PNG, SVG, JPG и т.д.)
|
|
||||||
'text/', # все текстовые файлы
|
|
||||||
'application/javascript',
|
|
||||||
'application/json',
|
|
||||||
'application/xml',
|
|
||||||
'application/rss+xml',
|
|
||||||
'application/x-web-app-manifest+json', # manifest
|
|
||||||
}
|
|
||||||
|
|
||||||
if mime_type and any(mime_type.lower().startswith(t) for t in inline_types):
|
|
||||||
response['Content-Disposition'] = 'inline'
|
|
||||||
else:
|
|
||||||
# Для остального (неизвестные типы, архивы и т.д.) — скачивание
|
|
||||||
response['Content-Disposition'] = f'attachment; filename="{file_path.name}"'
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_public_root_files():
|
|
||||||
"""Находит все обычные файлы в корне public/, кроме служебных артефактов (начинающихся с точки)."""
|
|
||||||
if not PUBLIC_ROOT.exists():
|
|
||||||
return
|
|
||||||
|
|
||||||
for file_path in sorted(PUBLIC_ROOT.iterdir()):
|
|
||||||
if not file_path.is_file():
|
|
||||||
continue
|
|
||||||
# Пропускаем служебные файлы (начинающиеся с точки)
|
|
||||||
if file_path.name.startswith('.'):
|
|
||||||
continue
|
|
||||||
yield file_path.name
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(ADMIN_URL, admin.site.urls),
|
path(ADMIN_URL, admin.site.urls),
|
||||||
|
|
||||||
@@ -161,26 +111,9 @@ urlpatterns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Динамическая генерация URL patterns для всех файлов в корне public/
|
|
||||||
# (кроме служебных файлов, начинающихся с точки)
|
|
||||||
# В DEV режиме и при ALLOW_MEDIA_SERVE=True отдаются через Django,
|
|
||||||
# в PRODUCTION это должно обслуживать Nginx/WhiteNoise
|
|
||||||
PUBLIC_ROOT_URLPATTERNS = [
|
|
||||||
path(filename, _serve_public_root_file, {'path': filename})
|
|
||||||
for filename in _iter_public_root_files()
|
|
||||||
]
|
|
||||||
|
|
||||||
# Добавляем static файлы для корня ДО основных URL patterns
|
|
||||||
# (чтобы отдавать файлы быстро и не проверять остальные рулы)
|
|
||||||
urlpatterns = [*PUBLIC_ROOT_URLPATTERNS, *urlpatterns]
|
|
||||||
|
|
||||||
handler404 = 'web.views.handler404'
|
|
||||||
handler400 = 'web.views.handler400'
|
|
||||||
handler403 = 'web.views.handler403'
|
|
||||||
handler500 = 'web.views.handler500'
|
|
||||||
|
|
||||||
# Для локального тестирования production конфига: отдача медиа через Django
|
# Для локального тестирования production конфига: отдача медиа через Django
|
||||||
# В реальном production медиа обслуживает Nginx!
|
# В реальном production медиа обслуживает Nginx!
|
||||||
|
import os
|
||||||
if DEBUG or env.bool('ALLOW_MEDIA_SERVE', default=False):
|
if DEBUG or env.bool('ALLOW_MEDIA_SERVE', default=False):
|
||||||
from django.views.static import serve as serve_static
|
from django.views.static import serve as serve_static
|
||||||
# Проверяем что директория медиа существует
|
# Проверяем что директория медиа существует
|
||||||
@@ -195,17 +128,9 @@ if DEBUG or env.bool('ALLOW_MEDIA_SERVE', default=False):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
# --- страничка для тестирования верстки текста в блоге
|
# --- страничка для тестирования верстки текста в блоге
|
||||||
urlpatterns += [re_path(r'^blog/tmp[/*]$', service.tmp),]
|
urlpatterns += [re_path(r'^blog/tmp[/*]$', service.tmp),]
|
||||||
# --- странички для тестирования страниц с кодами ошибок
|
|
||||||
urlpatterns += [
|
|
||||||
re_path(r'^400$', views.handler400),
|
|
||||||
re_path(r'^403$', views.handler403),
|
|
||||||
re_path(r'^404$', views.handler404),
|
|
||||||
re_path(r'^500$', views.handler500),
|
|
||||||
]
|
|
||||||
# ___ ____ _ _____ _ _ _____ _
|
# ___ ____ _ _____ _ _ _____ _
|
||||||
# | | | | \ ___| |_ _ _ ___ |_ _|___ ___| | |_ ___ ___ | _ |___ ___ ___| |
|
# | | | | \ ___| |_ _ _ ___ |_ _|___ ___| | |_ ___ ___ | _ |___ ___ ___| |
|
||||||
# |_ | | | | -_| . | | | . | | | | . | . | | . | .'| _| | __| .'| | -_| |
|
# |_ | | | | -_| . | | | . | | | | . | . | | . | .'| _| | __| .'| | -_| |
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>400 — Некорректный запрос</title>
|
|
||||||
<!-- Аналитика: Google Analytics 4, Yandex.Metrika, Top.Mail.Ru -->
|
|
||||||
<script src="/static/js/analytics.js" type="text/javascript"></script>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6; /* Полупрозрачность только для картинки */
|
|
||||||
z-index: -1; /* Уводит картинку под текст */
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/static/img/oknardia_logo.gif'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">400</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Некорректный запрос</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Сервер не может обработать ваш запрос. Пожалуйста, проверьте корректность данных.</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>401 — Требуется авторизация</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">401</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Требуется авторизация</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Для доступа к этому ресурсу требуется авторизация.</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/login-logout">Войти в систему</a> или <a href="/">вернуться на главную</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>403 — Доступ запрещён</title>
|
|
||||||
<!-- Аналитика: Google Analytics 4, Yandex.Metrika, Top.Mail.Ru -->
|
|
||||||
<script src="/static/js/analytics.js" type="text/javascript"></script>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6; /* Полупрозрачность только для картинки */
|
|
||||||
z-index: -1; /* Уводит картинку под текст */
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/static/img/oknardia_logo.gif'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">403</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Доступ запрещён</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">У вас нет прав для доступа к этому ресурсу. Если вы считаете это ошибкой, свяжитесь с администратором.</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>404 — Страница не найдена</title>
|
|
||||||
<!-- Аналитика: Google Analytics 4, Yandex.Metrika, Top.Mail.Ru -->
|
|
||||||
<script src="/static/js/analytics.js" type="text/javascript"></script>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6; /* Полупрозрачность только для картинки */
|
|
||||||
z-index: -1; /* Уводит картинку под текст */
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/static/img/oknardia_logo.gif'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">404</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Страница не найдена</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">К сожалению, запрашиваемая страница не существует или была удалена.</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>413 — Файл слишком большой</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">413</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Файл слишком большой</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Размер загружаемого файла превышает допустимый лимит (максимум 10 МБ).</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>429 — Слишком много запросов</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">429</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Слишком много запросов</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Вы отправляете слишком много запросов. Пожалуйста, немного подождите и попробуйте снова.</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>500 — Внутренняя ошибка сервера</title>
|
|
||||||
<!-- Аналитика: Google Analytics 4, Yandex.Metrika, Top.Mail.Ru -->
|
|
||||||
<script src="/static/js/analytics.js" type="text/javascript"></script>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6; /* Полупрозрачность только для картинки */
|
|
||||||
z-index: -1; /* Уводит картинку под текст */
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">500</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Внутренняя ошибка сервера</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">На нашем сервере произошла непредвиденная ошибка. Команда разработчиков уже работает над решением проблемы.</h4>
|
|
||||||
<p style="color: #999; margin: 20px 0 0 30px; font-size: 14px;">Если проблема сохраняется, пожалуйста, свяжитесь с администратором: <a href="mailto:info@oknardia.ru">info@oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>502 — Плохой шлюз</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">502</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Плохой шлюз</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Сервер приложения временно недоступен. Мы работаем над восстановлением работоспособности.</h4>
|
|
||||||
<p style="color: #999; margin: 20px 0 0 30px; font-size: 14px;">Пожалуйста, попробуйте перезагрузить страницу через несколько минут. Если проблема сохраняется, свяжитесь с нами: <a href="mailto:info@oknardia.ru">info@oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>503 — Сервис недоступен</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">503</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Сервис недоступен</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Сайт временно недоступен для плановых работ. Мы скоро вернёмся!</h4>
|
|
||||||
<p style="color: #999; margin: 20px 0 0 30px; font-size: 14px;">Информация об обслуживании обновляется автоматически. Спасибо за терпение!</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>504 — Тайм-аут шлюза</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 120px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">504</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Тайм-аут шлюза</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Сервер приложения слишком долго отвечает на запрос. Это может быть перегрузка или технический сбой.</h4>
|
|
||||||
<p style="color: #999; margin: 20px 0 0 30px; font-size: 14px;">Попробуйте перезагрузить страницу через несколько минут. Если проблема повторится, свяжитесь с поддержкой: <a href="mailto:info@oknardia.ru">info@oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ru">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Технические работы</title>
|
|
||||||
<style>
|
|
||||||
.container::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0; left: 0; right: 0; bottom: 0;
|
|
||||||
background-image: url('/media/img_seria/1LG-504D12_2.jpg');
|
|
||||||
opacity: 0.6;
|
|
||||||
z-index: -1;
|
|
||||||
background-position: 18% 15%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 200px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container" style="margin: 10% 0 0 20%;">
|
|
||||||
<h1 style="font-size: 80px; margin: 0; color: #ccc; background-image: url('/media/img_seria/1LG-504D12_2.jpg'); background-position: left 65%; background-repeat: no-repeat; background-size: 400px auto;"><i style="padding-left: 420px">⚙</i></h1>
|
|
||||||
<h2 style="color: #333; font-size: 40px;">Технические работы</h2>
|
|
||||||
<h4 style="color: #666; margin: 0 0 0 30px; font-size: 20px;">Приносим извинения. В данный момент сайт проходит техническое обслуживание. Мы скоро вернёмся!</h4>
|
|
||||||
<p style="margin-top: 55px;"><a href="/">Вернуться на главную oknardia.ru</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,13 +1,4 @@
|
|||||||
# Игнорируем все файлы в этой папке (генерируются динамически)
|
### Каталог для пре-рендер шаблонов
|
||||||
# Содержит кеш-файлы со статик-шаблонами:
|
# Игнорируем все файлы в этом каталоге, так как они генерируются автоматически
|
||||||
# - *_id_static_flaps.html
|
# и не должны быть включены в систему контроля версий.
|
||||||
# - *_id_static_graph.html
|
*.*
|
||||||
# - *_id_static_map_stats.html
|
|
||||||
#
|
|
||||||
# Эти файлы генерируются при запуске management-команды:
|
|
||||||
# python manage.py regenerate_seria_prerender
|
|
||||||
#
|
|
||||||
# В продакшене они сохраняются на диск и включаются в шаблоны
|
|
||||||
# за счет системы двухуровневого кеширования.
|
|
||||||
*
|
|
||||||
!.gitignore
|
|
||||||
|
|||||||
@@ -242,66 +242,3 @@ def get_address(request: HttpRequest) -> HttpResponse:
|
|||||||
'ticks': float(time.perf_counter() - time_start),
|
'ticks': float(time.perf_counter() - time_start),
|
||||||
})
|
})
|
||||||
return render(request, "popup/popup_show_apartment_variants.html", to_template)
|
return render(request, "popup/popup_show_apartment_variants.html", to_template)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ОБРАБОТЧИКИ ОШИБОК ДЛЯ ТЕСТИРОВАНИЯ В DEBUG РЕЖИМЕ
|
|
||||||
# ============================================================================
|
|
||||||
# Используется в urls.py при DEBUG=True для тестирования верстки страниц ошибок.
|
|
||||||
# Позволяет визуально проверить, как выглядят страницы 400, 403, 404 и 500
|
|
||||||
# без необходимости искусственно вызывать реальные ошибки.
|
|
||||||
|
|
||||||
|
|
||||||
def handler400(request: HttpRequest, exception=None) -> HttpResponse:
|
|
||||||
"""Отображает страницу ошибки 400 (Bad Request).
|
|
||||||
|
|
||||||
Используется только в DEBUG режиме для тестирования верстки.
|
|
||||||
|
|
||||||
:param request: входящий http-запрос
|
|
||||||
:param exception: исключение (если есть)
|
|
||||||
:return response: исходящий http-ответ с кодом 400
|
|
||||||
"""
|
|
||||||
response = render(request, "error/400.html", {})
|
|
||||||
response.status_code = 400
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def handler403(request: HttpRequest, exception=None) -> HttpResponse:
|
|
||||||
"""Отображает страницу ошибки 403 (Forbidden).
|
|
||||||
|
|
||||||
Используется только в DEBUG режиме для тестирования верстки.
|
|
||||||
|
|
||||||
:param request: входящий http-запрос
|
|
||||||
:param exception: исключение (если есть)
|
|
||||||
:return response: исходящий http-ответ с кодом 403
|
|
||||||
"""
|
|
||||||
response = render(request, "error/403.html", {})
|
|
||||||
response.status_code = 403
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def handler404(request: HttpRequest, exception=None) -> HttpResponse:
|
|
||||||
"""Отображает страницу ошибки 404 (Not Found).
|
|
||||||
|
|
||||||
Используется только в DEBUG режиме для тестирования верстки.
|
|
||||||
|
|
||||||
:param request: входящий http-запрос
|
|
||||||
:param exception: исключение (если есть)
|
|
||||||
:return response: исходящий http-ответ с кодом 404
|
|
||||||
"""
|
|
||||||
response = render(request, "error/404.html", {})
|
|
||||||
response.status_code = 404
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def handler500(request: HttpRequest) -> HttpResponse:
|
|
||||||
"""Отображает страницу ошибки 500 (Internal Server Error).
|
|
||||||
|
|
||||||
Используется только в DEBUG режиме для тестирования верстки.
|
|
||||||
|
|
||||||
:param request: входящий http-запрос
|
|
||||||
:return response: исходящий http-ответ с кодом 500
|
|
||||||
"""
|
|
||||||
response = render(request, "error/500.html", {})
|
|
||||||
response.status_code = 500
|
|
||||||
return response
|
|
||||||
|
|||||||
+1
-4
@@ -1,5 +1,5 @@
|
|||||||
# Маркетплейс-агрегатор цен на установку пластиковых и деревянных окон — ОКНАРДИЯ
|
# Маркетплейс-агрегатор цен на установку пластиковых и деревянных окон — ОКНАРДИЯ
|
||||||
# robots.txt последняя версия: 2026-05-20
|
# robots.txt последняя версия: 2026-05-15
|
||||||
|
|
||||||
User-Agent: *
|
User-Agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
@@ -10,9 +10,6 @@ Disallow: /*?*token*
|
|||||||
Disallow: /*.json$
|
Disallow: /*.json$
|
||||||
Disallow: /.*\.(js|css)$ # CSS и JS обслуживаются отдельно через CDN
|
Disallow: /.*\.(js|css)$ # CSS и JS обслуживаются отдельно через CDN
|
||||||
|
|
||||||
# Параметры, которые не влияют на смысловое содержимое
|
|
||||||
Clean-param: page-back /
|
|
||||||
|
|
||||||
# Быстрые краулеры
|
# Быстрые краулеры
|
||||||
User-agent: YandexBot
|
User-agent: YandexBot
|
||||||
Crawl-delay: 15
|
Crawl-delay: 15
|
||||||
|
|||||||
Reference in New Issue
Block a user