fix: корректная отдача статики из корня каталога public
This commit is contained in:
@@ -1,24 +1,13 @@
|
|||||||
# -*- 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, path, re_path
|
from django.urls import include, path, re_path
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
from django.http import FileResponse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import environ
|
import environ
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
# Инициализируем env
|
# Инициализируем env
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
@@ -27,6 +16,67 @@ 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),
|
||||||
|
|
||||||
@@ -111,6 +161,20 @@ 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]
|
||||||
|
|
||||||
|
|
||||||
# Для локального тестирования production конфига: отдача медиа через Django
|
# Для локального тестирования production конфига: отдача медиа через Django
|
||||||
# В реальном production медиа обслуживает Nginx!
|
# В реальном production медиа обслуживает Nginx!
|
||||||
import os
|
import os
|
||||||
@@ -128,6 +192,7 @@ 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),]
|
||||||
|
|||||||
Reference in New Issue
Block a user