5 Commits

Author SHA1 Message Date
e15017f3a6 add: Страницы обработки ошибок
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 42s
2026-05-21 00:42:54 +03:00
958f11f398 mod: Clean-param 2026-05-20 19:54:42 +03:00
3ab38b4885 mod: алиас sitemap sitemap.xml 2026-05-20 19:25:10 +03:00
4b038302c3 add: Добавлен алиас /sitemap.xml -> /media/_serv_sitemap/sitemap.xml в nginx конфиг
- Поисковики ищут sitemap.xml в корне сайта
- Nginx теперь проксирует /sitemap.xml на /media/_serv_sitemap/sitemap.xml
- Установлен кеш на 1 день (чаще обновляется, чем картинки)
2026-05-20 18:38:02 +03:00
3c10b490b3 minor: Улучшены комментарии nginx конфига и исправлен sed паттерн для корректной замены путей
- Уточнены комментарии про архитектуру (медиа, ошибки, sitemap через Django)
- Исправлен sed паттерн: было '/home/user/app/oknardia-site' -> '/home/user/path-to-oknardia-app'
- Улучшен www редирект для правильной подстановки домена
- Убраны дублирующиеся комментарии про требуемую замену
2026-05-20 18:35:07 +03:00
16 changed files with 442 additions and 35 deletions

View File

@@ -1,27 +1,20 @@
# config/nginx/oknardia-app--external-nginx.conf # config/nginx/oknardia-app--external-nginx.conf
# ============================================================================== # ==============================================================================
# ЭТАЛОННЫЙ КОНФИГУРАЦИОННЫЙ ФАЙЛ NGINX (Reverse Proxy для Docker) # КОНФИГУРАЦИОННЫЙ ФАЙЛ NGINX (Reverse Proxy для Docker + Production)
# ============================================================================== # ==============================================================================
# #
# ВНИМАНИЕ: # ИНФОРМАЦИЯ:
# Этот файл является шаблоном. При первом деплое он копируется # 1. Этот файл используется как шаблон при деплое
# в `/home/user/path-to-oknardia-app/config/nginx/oknardia-app--external-nginx.conf`, # 2. При первом деплое пути `/home/user/path-to-oknardia-app` заменяются на реальный путь через sed
# а затем (уже на хосте, руками) через силинк в `/etc/nginx/sites-available/` и активируется. # 3. Сгенерированный конфиг скопируется в `/etc/nginx/sites-available/oknardia`
# При последующих деплоях `oknardia-app--external-nginx.conf` НЕ ПЕРЕЗАПИСЫВАЕТСЯ, # 4. Последующие деплои ОБНОВЛЯЮТ этот файл автоматически (sed + копирование)
# чтобы не затереть SSL-сертификаты и ручные правки.
# #
# Если вы изменили этот файл в репозитории и хотите применить изменения на проде: # АРХИТЕКТУРА:
# вам нужно обновить файл в `/home/user/path-to-oknardia-app/config/nginx/oknardia-app--external-nginx.conf` вручную (diff + copy). # - Nginx (порты 80/443) <-> Gunicorn контейнер (localhost:8000)
# # - Медиа файлы отдаются из `/home/user/path-to-oknardia-app/media/` напрямую
# Так же (рядом) будет создан образец этого файла `nginx_oknardia.conf.example`, который будет обновляться при деплоях # - Ошибки 5xx берутся из `media/_error` (копируются контейнером при старте)
# из репозитория, чтобы вы могли видеть, что изменилось и при необходимости перенести эти изменения на прод. # - 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 {
@@ -70,22 +63,28 @@ 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.
# Эти файлы должны лежать в папке, доступной Nginx (например, в `media/_error`). # Эти файлы копируются в `media/_error` при старте контейнера Docker.
# #
# ВАЖНО: # ВАЖНО: 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;
@@ -96,8 +95,6 @@ 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;
@@ -112,7 +109,6 @@ 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; }
@@ -141,10 +137,11 @@ server {
} }
} }
# 3. Редирект с www на без-www (SEO best practice) # 3. Редирект с www на основной домен (SEO best practice)
server { server {
server_name www.tmp.oknardia.ru; server_name www.tmp.oknardia.ru;
listen 80; listen 80;
return 301 $scheme://oknardia.ru$request_uri; # Всегда редиректим на основной домен listen [::]:80;
return 301 $scheme://tmp.oknardia.ru$request_uri;
} }

View File

@@ -40,11 +40,17 @@ services:
python manage.py regenerate_seria_prerender && python manage.py regenerate_seria_prerender &&
python manage.py make_rating && python manage.py make_rating &&
mkdir -p /nginx_configs_host/nginx && mkdir -p /nginx_configs_host/nginx &&
sed \"s|/home/user/app/oknardia-site|$${HOST_PROJECT_PATH}|g\" /home/app/config/nginx/oknardia-app--external-nginx.conf > /nginx_configs_host/nginx/oknardia-app--external-nginx.conf.example && 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 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; 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'; echo 'INIT: Created new nginx config with correct paths';
fi && 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" python -m gunicorn --workers 2 --bind 0.0.0.0:8000 --timeout 120 oknardia.wsgi:application"
# Пробрасывание портов # Пробрасывание портов

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""oknardia Конфигурация URL""" """oknardia Конфигурация URL"""
from django.contrib import admin from django.contrib import admin
from django.urls import include, path, re_path from django.urls import include, re_path, path
from django.conf.urls.static import static
from django.http import FileResponse from django.http import FileResponse
from pathlib import Path from pathlib import Path
import environ import environ
import mimetypes import mimetypes
import os
# Инициализируем env # Инициализируем env
env = environ.Env() env = environ.Env()
@@ -174,10 +174,13 @@ PUBLIC_ROOT_URLPATTERNS = [
# (чтобы отдавать файлы быстро и не проверять остальные рулы) # (чтобы отдавать файлы быстро и не проверять остальные рулы)
urlpatterns = [*PUBLIC_ROOT_URLPATTERNS, *urlpatterns] 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
# Проверяем что директория медиа существует # Проверяем что директория медиа существует
@@ -196,6 +199,13 @@ 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),
]
# ___ ____ _ _____ _ _ _____ _ # ___ ____ _ _____ _ _ _____ _
# | | | | \ ___| |_ _ _ ___ |_ _|___ ___| | |_ ___ ___ | _ |___ ___ ___| | # | | | | \ ___| |_ _ _ ___ |_ _|___ ___| | |_ ___ ___ | _ |___ ___ ___| |
# |_ | | | | -_| . | | | . | | | | . | . | | . | .'| _| | __| .'| | -_| | # |_ | | | | -_| . | | | . | | | | . | . | | . | .'| _| | __| .'| | -_| |

View File

@@ -0,0 +1,31 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,32 @@
<!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>

View File

@@ -0,0 +1,31 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,31 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -0,0 +1,29 @@
<!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>

View File

@@ -242,3 +242,66 @@ 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

View File

@@ -1,5 +1,5 @@
# Маркетплейс-агрегатор цен на установку пластиковых и деревянных окон — ОКНАРДИЯ # Маркетплейс-агрегатор цен на установку пластиковых и деревянных окон — ОКНАРДИЯ
# robots.txt последняя версия: 2026-05-15 # robots.txt последняя версия: 2026-05-20
User-Agent: * User-Agent: *
Allow: / Allow: /
@@ -10,6 +10,9 @@ 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