Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a537bc9c37 | |||
| a62e02ddcb | |||
| d294e5a951 | |||
| fd5553e326 | |||
| 5b5e8dd096 | |||
| 906888ae73 | |||
| ac9109ea6e | |||
| 9027f12792 | |||
| 4ddd787f5d | |||
| a60972d70b | |||
| 8fa96cae66 | |||
| 7f96932a40 | |||
| d59c867010 | |||
| 9ea851ad95 | |||
| 5146f88c7d | |||
| 20ecb9cc4c | |||
| ea2d352cae | |||
| d459eedf41 | |||
| 9d31429e14 | |||
| c5963d1d30 | |||
| 12001bc749 | |||
| 99a2ace43f | |||
| 81efaf1ba5 | |||
| 42b378fcbc | |||
| ba4175dfdb | |||
| 4194b351d2 | |||
| be68a82927 | |||
| 53b127a966 | |||
| 746c50a988 | |||
| 2520362ad5 | |||
| a857101c3f | |||
| 7eeb44a1f5 | |||
| 86bfd9b07b | |||
| c3c81d7ff5 | |||
| f4cce3d08a | |||
| 45275c51f6 | |||
| f2f98d9229 | |||
| a33b517a3e | |||
| d4624e7761 | |||
| a608dea61f |
@@ -61,5 +61,11 @@ jobs:
|
||||
# Собираем под текущую архитектуру (linux/amd64).
|
||||
# Если сервер и MacMini на разных архитектурах (x86 vs ARM), добавьте нужные, например: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
# platforms: linux/amd64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# ДОБАВЛЕНО:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
# И это для медленного интернета:
|
||||
timeout: 900 # 15 минут на всю сборку
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -262,8 +262,17 @@ cython_debug/
|
||||
public/media/*
|
||||
!public/media/README.md
|
||||
|
||||
# OS specific
|
||||
# OS specific (Synology DSM)
|
||||
.DS_Store
|
||||
|
||||
# Data Backup
|
||||
database/data.json
|
||||
|
||||
# База данных SQLite
|
||||
# Никогда не копировать локальную базу в образ!
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
db.sqlite3-shm
|
||||
db.sqlite3-wal
|
||||
*.sqlite3
|
||||
*.sqlite3-journal
|
||||
129
Dockerfile
129
Dockerfile
@@ -1,63 +1,108 @@
|
||||
# ==========================================
|
||||
# Dockerfile для Django + Gunicorn + WhiteNoise
|
||||
# ==========================================
|
||||
# =================================================
|
||||
# STAGE 1: Builder - Установка зависимостей
|
||||
# =================================================
|
||||
FROM python:3.12-slim AS builder
|
||||
|
||||
# 1. Базовый образ: Python 3.12 (Slim версия для меньшего размера)
|
||||
FROM python:3.12-slim
|
||||
|
||||
# 2. Переменные окружения для Python
|
||||
# PYTHONDONTWRITEBYTECODE: Запрещает Python писать .pyc файлы
|
||||
# PYTHONUNBUFFERED: Гарантирует, что вывод консоли (logs) виден сразу (не буферизуется)
|
||||
# Устанавливаем переменные окружения
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
# Poetry настройки: не создавать виртуальное окружение внутри контейнера (ставим системно).
|
||||
# Дублирует `poetry config virtualenvs.create false` в пп.7 (на всякий случай).
|
||||
# Говорим Poetry, чтобы он не создавал venv, а ставил пакеты в системный site-packages
|
||||
ENV POETRY_VIRTUALENVS_CREATE=false
|
||||
# Путь настройки Django (по умолчанию для production) на случай если контейнер будет запущен не через docker-compose.
|
||||
ENV DJANGO_SETTINGS_MODULE=dicquo.settings
|
||||
|
||||
# 3. Рабочая директория внутри контейнера
|
||||
WORKDIR /app
|
||||
|
||||
# 4. Установка системных зависимостей
|
||||
# - libjpeg-dev zlib1g-dev: библиотеки для работы с изображениями (Pillow)
|
||||
# Устанавливаем системные зависимости, необходимые для СБОРКИ пакетов (например, Pillow)
|
||||
# build-essential нужен для компиляции, -dev пакеты для сборки Pillow
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
libjpeg-dev \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 5. Установка Poetry через pip (быстро и надежно)
|
||||
# Устанавливаем Poetry
|
||||
RUN pip install --no-cache-dir poetry
|
||||
|
||||
# 6. Копируем файлы зависимостей (pyproject.toml и poetry.lock)
|
||||
# Делаем это ДО копирования всего кода, чтобы использовать кэш Docker layers.
|
||||
# Создаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем только файлы зависимостей для кэширования этого слоя
|
||||
COPY pyproject.toml poetry.lock /app/
|
||||
|
||||
# 7. Установка зависимостей проекта
|
||||
# --no-interaction: не будет спрашивать подтверждения
|
||||
# --no-ansi: уберваем цветные символы из логов сборки (они иногда мусорят)
|
||||
# --no-root: не устанавливать сам проект как пакет (мы просто копируем код)
|
||||
# --only main: не ставить dev-зависимости (тесты, линтеры и т.п.) для продакшена
|
||||
# RUN poetry install --no-root --only main
|
||||
# Настройка Poetry: не создавать venv и установка зависимостей (без dev-зависимостей для продакшена)
|
||||
RUN poetry config virtualenvs.create false \
|
||||
&& poetry install --no-interaction --no-ansi --no-root --only main
|
||||
# Устанавливаем зависимости проекта. Poetry установит их в /usr/local/lib/python3.12/site-packages
|
||||
RUN poetry install --no-interaction --no-ansi --no-root --only main
|
||||
|
||||
# 8. Копируем весь исходный код проекта в контейнер
|
||||
COPY . /app/
|
||||
# Очищаем кэш Poetry, чтобы уменьшить размер слоя
|
||||
RUN poetry cache clear --all -n
|
||||
|
||||
# 9. Сборка статики (CSS, JS)
|
||||
# Важно: Запускаем collectstatic с фейковым SECRET_KEY, так как на этапе сборки env файла может не быть.
|
||||
RUN SECRET_KEY=dummy_build_key python dicquo/manage.py collectstatic --noinput --clear
|
||||
|
||||
# 10. Открываем порт 8000
|
||||
# =================================================
|
||||
# STAGE 2: Final - Создание чистого и безопасного образа
|
||||
# =================================================
|
||||
FROM python:3.12-slim AS stage-final
|
||||
|
||||
# Устанавливаем переменные окружения
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV DJANGO_SETTINGS_MODULE=dicquo.settings
|
||||
|
||||
# Устанавливаем только RUNTIME системные зависимости.
|
||||
# Пакеты -dev и build-essential здесь не нужны.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libjpeg62-turbo \
|
||||
sqlite3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Создаем пользователя без прав root для безопасности
|
||||
# RUN addgroup --system app && adduser --system --ingroup app app
|
||||
|
||||
# Создаем рабочую директорию
|
||||
WORKDIR /home/app/web
|
||||
|
||||
# Копируем установленные Python-пакеты из builder-стадии
|
||||
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||
|
||||
# Копируем исполняемые файлы (gunicorn, pip и т.д.)
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
|
||||
# Копируем исходный код проекта и устанавливаем правильного владельца
|
||||
# ИЗМЕНЕНИЕ: app:app -> 1000:1000
|
||||
COPY --chown=1000:1000 . .
|
||||
|
||||
# Создаём директорию для конфигов nginx и даём права пользователю app
|
||||
# Это выполняется ещё от root, поэтому проблем с permissions не будет.
|
||||
RUN mkdir -p /nginx_configs_host/nginx && chown -R 1000:1000 /nginx_configs_host
|
||||
|
||||
# Создаём директорию для собранной статики и даём права пользователю app
|
||||
RUN mkdir -p /home/app/web/staticfiles && chown -R 1000:1000 /home/app/web/staticfiles
|
||||
|
||||
# Создаём директорию для ошибок (404, 500) и даём права пользователю app
|
||||
RUN mkdir -p /app/public/media/errors && chown -R 1000:1000 /app/public/media
|
||||
|
||||
# Создаём директорию для БД и даём права пользователю app
|
||||
# Это важно когда БД монтируется как том с хоста
|
||||
RUN mkdir -p /app/database && chown -R 1000:1000 /app/database
|
||||
|
||||
# Переключаемся на пользователя без прав root
|
||||
USER 1000
|
||||
|
||||
|
||||
# Собираем статику
|
||||
# Используем dummy ключ, так как .env файла нет на этапе сборки
|
||||
RUN SECRET_KEY=dummy python dicquo/manage.py collectstatic --noinput --clear
|
||||
|
||||
# Открываем порт
|
||||
EXPOSE 8000
|
||||
|
||||
# 11. Команда запуска
|
||||
# Переходим в подпапку dicquo, где лежит код Django проекта
|
||||
WORKDIR /app/dicquo
|
||||
# Проверка здоровья контейнера
|
||||
# Docker будет периодически проверять, жив ли контейнер, отправляя GET запрос к главной странице.
|
||||
# Параметры:
|
||||
# --interval=30s - проверка каждые 30 секунд
|
||||
# --timeout=3s - ожидаем ответ максимум 3 секунды
|
||||
# --start-period=10s - даем контейнеру 10 секунд на запуск перед первой проверкой
|
||||
# --retries=3 - объявляем контейнер unhealthy после 3 неудачных попыток
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()" || exit 1
|
||||
|
||||
# Запускаем Gunicorn (по умолчанию, если не переопределено в docker-compose) на три воркера, привязывая его к
|
||||
# порту 8000 и указывая на точку входа приложения (wsgi.py).
|
||||
CMD ["gunicorn", "--workers", "3", "--bind", "0.0.0.0:8000", "dicquo.wsgi:application"]
|
||||
# Переходим в директорию с manage.py для корректного запуска gunicorn
|
||||
WORKDIR /home/app/web/dicquo
|
||||
|
||||
# Команда запуска
|
||||
CMD ["gunicorn", "--workers", "3", "--bind", "0.0.0.0:8000", "dicquo.wsgi:application"]
|
||||
45
PLANS.md
Normal file
45
PLANS.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Планы по развитию проекта (DicQuo)
|
||||
|
||||
## 1. Список Авторов (Feature: Authors List)
|
||||
**Цель:** Улучшить SEO (плоская структура) и навигацию, сохранив "Дзен" (минимализм).
|
||||
|
||||
**Концепция:**
|
||||
- Добавить иконку "Люди/Авторы" в шапку сайта (рядом с бургером).
|
||||
- По клику открывается **полноэкранный оверлей** (как статистика).
|
||||
- Внутри список авторов карточками/строками.
|
||||
|
||||
**Элементы списка:**
|
||||
1. **Имя Автора** (крупно) -> Ссылка на ротацию цитат автора (`/?tag=author-slug`).
|
||||
2. **Счетчик цитат** (мелко, например `(25)`) -> Клик раскрывает "гармошку" (аккордеон).
|
||||
3. **Список цитат** (внутри гармошки) -> Прямые ссылки на цитаты (например: `/123_nachalo-frazy...`). Текст ссылок — начало фразы.
|
||||
|
||||
**Техническая реализация:**
|
||||
- **Backend:** `Context Processor` или логика в `IndexView` (или отдельный AJAX endpoint) для сбора данных:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "Имя",
|
||||
"slug": "slug",
|
||||
"count": 25,
|
||||
"quotes": [{"id": 1, "url": "...", "text": "Текст..."}, ...]
|
||||
}, ...
|
||||
]
|
||||
```
|
||||
- **Frontend:** HTML/CSS для модального окна и JS для раскрытия списков.
|
||||
|
||||
## 2. Админка
|
||||
- Починить мелкие баги в управлении тегами.
|
||||
- Улучшить управление настройками типографа (etpgrf) через виртуальные поля.
|
||||
- Поля в админке для настройки (кавычки, неразрывные пробелы и т.д.).
|
||||
- При сохранении применять типограф к полям `szContent` -> `szContentHTML`.
|
||||
- `szContentHTML` сделать редактитруемым чекрез CodeMirror (для ручной типографики тяжёлых случаев).
|
||||
|
||||
## 3. SEO и Оптимизация
|
||||
- Проверить индексацию новых страниц `static_404`/`static_500`.
|
||||
- Убедиться, что `canonical` ссылки работают корректно.
|
||||
|
||||
## 4. Дальние планы
|
||||
- Форма для добавления цитат пользователями (с модерацией).
|
||||
- API для интеграции с внешними сервисами (магазинами грампластинок и музыкальными сервисами).
|
||||
- Сбор цитат из открытых источников (например, с помощью парсинга сайтов с цитатами или API).
|
||||
|
||||
@@ -76,18 +76,24 @@ server {
|
||||
}
|
||||
|
||||
# --- СТРАНИЦЫ ОШИБОК (Custom Error Pages) ---
|
||||
# Если Django упал (502) или файл из media не найден Nginx-ом (404), показываем наши красивые заглушки.
|
||||
# Файлы копируются в media/errors при старте контейнера.
|
||||
# ТРЕБУЕТСЯ ЗАМЕНА ПРИ ДЕПЛОЕ: /home/user/app/dq-site -> ваш реальный путь
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /500.html;
|
||||
# Если Django упал (502) или сработал тайм-аут (504), Nginx должен отдать статический HTML.
|
||||
# Эти файлы должны лежать в папке, доступной Nginx (например, в media/errors).
|
||||
#
|
||||
# ВАЖНО:
|
||||
# 1. Файлы 50x.html (500, 502, 503, 504) копируются в media/errors при старте контейнера (см. docker-compose.prod.yml -> command).
|
||||
# 2. error_page директива перехватывает ошибки от апстрима (Gunicorn).
|
||||
|
||||
location = /404.html {
|
||||
error_page 500 502 503 504 /500.html;
|
||||
# (Опционально) 404 тоже можно кастомизировать, но обычно Django сам отдает 404.
|
||||
# Nginx отдаст эту страницу только если сам не найдет статику.
|
||||
error_page 404 /404.html;
|
||||
|
||||
location = /500.html {
|
||||
root /home/user/app/dq-site/media/errors;
|
||||
internal;
|
||||
}
|
||||
|
||||
location = /500.html {
|
||||
location = /404.html {
|
||||
root /home/user/app/dq-site/media/errors;
|
||||
internal;
|
||||
}
|
||||
@@ -102,7 +108,14 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Если нужно чтобы Django обрабатывал и HTTP, и HTTPS, то можно раскомментировать эту строку
|
||||
# и передавать реальный протокол от клиента
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Явно указываем https, потому что клиент всегда приходит по HTTPS к Nginx
|
||||
# Даже если внутри контейнера это HTTP на 127.0.0.1:8010, для Django это должно быть HTTPS
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
|
||||
# Тайм-ауты (важно для долгих операций, если они есть)
|
||||
proxy_read_timeout 180s;
|
||||
|
||||
Binary file not shown.
@@ -58,6 +58,7 @@ INSTALLED_APPS: list[str] = [
|
||||
'django.contrib.sites',
|
||||
'django.contrib.sitemaps',
|
||||
'taggit.apps.TaggitAppConfig',
|
||||
'django_select2',
|
||||
'web.apps.WebConfig',
|
||||
]
|
||||
|
||||
@@ -78,6 +79,13 @@ DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR.parent / 'database/db.sqlite3',
|
||||
'OPTIONS': {
|
||||
# Таймаут ожидания блокировки SQLite (в секундах)
|
||||
# ВАЖНО: Увеличен до 60 сек для работы с несколькими воркерами Gunicorn
|
||||
'timeout': 60,
|
||||
# Дополнительные опции для лучшей работы SQLite при concurrent доступе
|
||||
'init_command': "PRAGMA journal_mode=WAL;", # Write-Ahead Logging для лучшей concurrency
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,3 +156,12 @@ if not DEBUG:
|
||||
WHITENOISE_ROOT = BASE_DIR.parent / 'public'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Настройки безопасности для работы за прокси
|
||||
if not DEBUG:
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
USE_X_FORWARDED_HOST = True
|
||||
USE_X_FORWARDED_PORT = True
|
||||
|
||||
@@ -15,7 +15,7 @@ Including another URLconf
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.urls import path, re_path, include
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.sitemaps.views import sitemap
|
||||
from django.views.generic import TemplateView
|
||||
@@ -33,6 +33,7 @@ urlpatterns = [
|
||||
re_path(r'^$', views.IndexView.as_view()),
|
||||
re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.DictumDetailView.as_view()),
|
||||
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
|
||||
path("select2/", include("django_select2.urls")),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
@@ -42,4 +43,7 @@ if settings.DEBUG:
|
||||
path('500/', TemplateView.as_view(template_name="500.html")),
|
||||
path('403/', TemplateView.as_view(template_name="403.html")),
|
||||
path('400/', TemplateView.as_view(template_name="400.html")),
|
||||
# Для проверки статических страниц ошибок (Nginx)
|
||||
path('static_404/', TemplateView.as_view(template_name="static_404.html")),
|
||||
path('static_500/', TemplateView.as_view(template_name="static_500.html")),
|
||||
]
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block Title %}400: Плохой запрос{% endblock %}
|
||||
|
||||
{% block CONTENT %}{% include "blocks/header_nav.html" %}
|
||||
<div class="container main-content">
|
||||
<!-- Осно<D0BD><D0BE>ной контент: Текст + Картинка -->
|
||||
<div class="content-row">
|
||||
|
||||
<!-- Текстовая колонка -->
|
||||
<div class="text-col">
|
||||
<!-- Цитата -->
|
||||
<blockquote id="bb" style="border:none; margin:0; padding:0;">
|
||||
<span style="margin-left:-0.44em;">«</span>Вы спрашиваете меня о чем-то странном. Я не понимаю ваш запрос.»
|
||||
</blockquote>
|
||||
|
||||
<!-- Автор -->
|
||||
<div id="author">
|
||||
<cite>Озадаченный Сервер (400)</cite>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Блок тегов и навигации -->
|
||||
<div class="tags">
|
||||
<a href="/">Сформулировать иначе (на главную)</a>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>400: Плохой запрос | DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Загадочно:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Вы спрашиваете меня о чем-то странном. Я не понимаю
|
||||
ваш запрос.»
|
||||
</blockquote>
|
||||
<cite>Озадаченный Сервер (400)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="/">Сформулировать иначе (на главную)</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,31 +1,43 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block Title %}403: Доступ запрещен{% endblock %}
|
||||
|
||||
{% block CONTENT %}{% include "blocks/header_nav.html" %}
|
||||
<div class="container main-content">
|
||||
<!-- Основной контент: Текст + Картинка -->
|
||||
<div class="content-row">
|
||||
|
||||
<!-- Текстовая колонка -->
|
||||
<div class="text-col">
|
||||
<!-- Цитата -->
|
||||
<blockquote id="bb" style="border:none; margin:0; padding:0;">
|
||||
<span style="margin-left:-0.44em;">«</span>Вам сюда нельзя. Даже если очень хочется. Уходите!»
|
||||
</blockquote>
|
||||
|
||||
<!-- Автор -->
|
||||
<div id="author">
|
||||
<cite>Строгий Вахтёр (403)</cite>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Блок тегов и навигации -->
|
||||
<div class="tags">
|
||||
<a href="/">Уйти по-добру по-здорову</a>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>403: Доступ запрещен | DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Категорически:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Вам сюда нельзя. Даже если очень хочется. Уходите!»
|
||||
</blockquote>
|
||||
<cite>Строгий Вахтёр (403)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="/">Уйти по-добру по-здорову</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,31 +1,43 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block Title %}404: Страница не найдена{% endblock %}
|
||||
|
||||
{% block CONTENT %}{% include "blocks/header_nav.html" %}
|
||||
<div class="container main-content">
|
||||
<!-- Основной контент: Текст + Картинка -->
|
||||
<div class="content-row">
|
||||
|
||||
<!-- Текстовая колонка -->
|
||||
<div class="text-col">
|
||||
<!-- Цитата -->
|
||||
<blockquote id="bb" style="border:none; margin:0; padding:0;">
|
||||
<span style="margin-left:-0.44em;">«</span>Я искал везде. Даже под диваном. Этой страницы здесь нет.»
|
||||
</blockquote>
|
||||
|
||||
<!-- Автор -->
|
||||
<div id="author">
|
||||
<cite>Системный Администратор (404)</cite>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Блок тегов и навигации -->
|
||||
<div class="tags">
|
||||
<a href="/">Вернуться на главную</a>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>404: Страница не найдена | DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Вздыхая:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Я искал везде. Даже под диваном. Этой страницы здесь нет.»
|
||||
</blockquote>
|
||||
<cite>Системный Администратор (404)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="/">Вернуться на главную</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block Title %}500: Ошибка сервера{% endblock %}
|
||||
|
||||
{% block CONTENT %}{% include "blocks/header_nav.html" %}
|
||||
<div class="container main-content">
|
||||
<!-- Основной контент: Текст + Картинка -->
|
||||
<div class="content-row">
|
||||
|
||||
<!-- Текстовая колонка -->
|
||||
<div class="text-col">
|
||||
<!-- Цитата -->
|
||||
<blockquote id="bb" style="border:none; margin:0; padding:0;">
|
||||
<span style="margin-left:-0.44em;">«</span>Что-то пошло не так. Кажется, я уронил сервер. Подождите, пока я его подниму.»
|
||||
</blockquote>
|
||||
|
||||
<!-- Автор -->
|
||||
<div id="author">
|
||||
<cite>Системный Администратор (500)</cite>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Блок тегов и навигации -->
|
||||
<div class="tags">
|
||||
<a href="/">Попробовать обновить страницу</a>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>500: Ошибка сервера DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Неожиданно:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Что-то пошло не так. Кажется, я уронил сервер.
|
||||
Подождите, пока я его подниму.»
|
||||
</blockquote>
|
||||
<cite>Системный Администратор (500)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="/">Попробовать обновить страницу</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
<meta property="og:title" content="{% block OgTitle %}{{ DQ.szContent|truncatechars:85 }}{% endblock %}"/>
|
||||
<meta property="og:description" content="{% block OgDescription %}{{ DQ.szIntro|default:'' }} {{ DQ.szContent }} {{ AUTHOR.szAuthor|default:'' }}{% endblock %}"/>
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}"/>
|
||||
<meta property="og:site_name" content="DicQuo"/>
|
||||
{% if IMAGE %}<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ IMAGE.url }}"/>{% endif %}
|
||||
<meta property="og:site_name" content="DicQuo"/>{% if IMAGE %}
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ IMAGE.url }}"/>{% endif %}
|
||||
{# Шутка #}<meta name="generator" content="Microsoft FrontPage 1.0"/>
|
||||
{# Canonical #}<link rel="canonical" href="{{ request.build_absolute_uri }}"/>
|
||||
{# Canonical #}{% if DQ and DQ_SLUG %}<link rel="canonical" href="{{ request.scheme }}://{{ request.get_host }}/{{ DQ.id }}_{{ DQ_SLUG }}"/>{% else %}<link rel="canonical" href="{{ request.build_absolute_uri }}"/>{% endif %}
|
||||
{# Favicons #}<link rel="shortcut icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}"/>
|
||||
<link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}"/>
|
||||
{# Technical Meta #}<meta http-equiv="Last-Modified" content="{% block Last4Meta %}{% endblock %}"/>
|
||||
{# Для ИИ #}<link rel="help" type="text/markdown" href="/llms.txt"/>
|
||||
{# CSS #}<link rel="stylesheet" href="{% static 'css/dicquo.css' %}"/>
|
||||
<noscript><style>body { opacity: 1; }</style></noscript>{# Показать все если JS не поддерживатся #}
|
||||
{% block ExtraHead %}{# Если нужно что=то добавить в `<head>` #}{% endblock %}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
{% load static %}<script src="{% static 'js/counters.js' %}"></script>
|
||||
<noscript><div><img src="https://top-fwz1.mail.ru/counter?id=3744288;js=na" class="counter-pixel" alt="Top.Mail.Ru"/></div></noscript>
|
||||
<noscript><div><img src="https://top-fwz1.mail.ru/counter?id=3744288;js=na" class="counter-pixel" alt=""/></div><div><img src="https://mc.yandex.ru/watch/106953063" class="counter-pixel" alt="" /></div></noscript>
|
||||
@@ -1,27 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</div>
|
||||
<a href="/">Вернуться на главную</a>
|
||||
<cite>Системный Администратор (404)</cite>
|
||||
</blockquote>
|
||||
<span>«</span>Я искал везде. Даже под диваном. Этой страницы здесь нет.»
|
||||
<blockquote>
|
||||
<div class="container">
|
||||
<body>
|
||||
</head>
|
||||
</style>
|
||||
a:hover { color: #999; border-bottom: 1px solid #999; }
|
||||
a { color: #555; text-decoration: none; border-bottom: 1px dotted #555; transition: color 0.3s; font-size: 0.8em; margin-top: 30px; display: inline-block;}
|
||||
cite { display: block; font-size: 0.9em; color: #777; margin-top: 15px; font-style: normal;}
|
||||
blockquote span { margin-left: -0.44em; }
|
||||
blockquote { font-size: 2em; margin: 0 0 20px 0; font-style: italic; line-height: 1.4; }
|
||||
.container { max-width: 600px; padding: 20px; }
|
||||
body { background-color: #111; color: #ccc; font-family: Georgia, serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; }
|
||||
<style>
|
||||
<title>404: Страница не найдена</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<head>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>404: Страница не найдена | DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Озадачено:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Я искал везде. Даже под диваном. Этой страницы здесь нет.»
|
||||
</blockquote>
|
||||
<cite>Системный Администратор (404)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="/">Вернуться на главную</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,27 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>500: Ошибка сервера</title>
|
||||
<style>
|
||||
body { background-color: #111; color: #ccc; font-family: Georgia, serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; }
|
||||
.container { max-width: 600px; padding: 20px; }
|
||||
blockquote { font-size: 2em; margin: 0 0 20px 0; font-style: italic; line-height: 1.4; }
|
||||
blockquote span { margin-left: -0.44em; }
|
||||
cite { display: block; font-size: 0.9em; color: #777; margin-top: 15px; font-style: normal; }
|
||||
a { color: #555; text-decoration: none; border-bottom: 1px dotted #555; transition: color 0.3s; font-size: 0.8em; margin-top: 30px; display: inline-block;}
|
||||
a:hover { color: #999; border-bottom: 1px solid #999; }
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>500: Ошибка сервера | DicQuo</title>
|
||||
<style>
|
||||
body { margin: 0; min-height: 100vh; background-color: #211; color: silver; font-family: serif; opacity: 1; transition: opacity 0.9s ease-in-out; }
|
||||
header { display: flex; justify-content: space-between; align-items: center; padding: 1vh 4vw; }
|
||||
header > #logo { margin-top: 1vh; float: left; }
|
||||
header > #logo a { border: none; text-decoration: none; color: silver;}
|
||||
main { padding: 1vh 8vw; display: flex; flex-direction: column; justify-content: center; min-height: 60vh; }
|
||||
main > article { display: flex; align-items: center; justify-content: center; gap: 2vw; }
|
||||
main > article > figure > p { color: silver; font-size: 3vmin; line-height: 3.5vmin; padding-bottom: 2vmin; font-style: italic; }
|
||||
main > article > figure > blockquote { color: whitesmoke; font-size: 4.5vmin; line-height: 5vmin; border:none; margin:0; padding:0; }
|
||||
main > article > figure > cite { color: silver; font-size: 3.5vmin; line-height: 4vmin; text-align: right; padding-top: 8vmin; font-style: italic; display: block; }
|
||||
.tags { text-align: center; color: silver; font-size: 1.5vmin; line-height: 1.9vmin; padding: 1vh 8vw; float: left;}
|
||||
.tags a { text-decoration: none; position: relative; padding: 0 0.5ex; display: inline-block; color: white; border-bottom: dotted 1px silver; background: linear-gradient(to right, rgba(255, 255, 255, 0.9) 40%, slategray, silver, lightyellow 50%, rgba(255, 255, 255, 0.4)); background-clip: initial; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 250% 100%; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; background-position: 100%; transition: background-position 0.65s ease; margin-right: 2vmin; }
|
||||
.tags a:hover { color: white; background-position: 0 100%; border-bottom: solid 1px white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<blockquote>
|
||||
<span>«</span>Что-то пошло не так. Кажется, я уронил сервер, контейнер или случилось что-то ещё. Подождите, пока я его подниму.»
|
||||
</blockquote>
|
||||
<cite>Системный Администратор (5xx)</cite>
|
||||
<a href="#" onclick="window.location.reload(); return false;">Попробовать обновить через 5 минут.</a>
|
||||
</div>
|
||||
<header>
|
||||
<div id="logo">
|
||||
<a href="/" style="font-size: 3em; font-weight: bold; color: orange; font-style: italic">DQ</a>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<article>
|
||||
<figure>
|
||||
<p>Неожиданно:</p>
|
||||
<blockquote id="bb">
|
||||
<span style="margin-left:-0.44em;">«</span>Что-то пошло не так. Кажется, я уронил сервер.
|
||||
Подождите, пока я его подниму.»
|
||||
</blockquote>
|
||||
<cite>Системный Администратор (500)</cite>
|
||||
</figure>
|
||||
</article>
|
||||
</main>
|
||||
<div class="tags">
|
||||
<a href="#" onclick="window.location.reload(); return false;">Попробовать обновить страницу</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
from django.contrib import admin
|
||||
from django import forms
|
||||
from web.models import TbDictumAndQuotes, TbAuthor, TbImages, TbOrigin
|
||||
from taggit.managers import TaggableManager
|
||||
from django_select2.forms import Select2TagWidget
|
||||
from taggit.models import Tag
|
||||
from taggit.utils import parse_tags
|
||||
from django.db import models
|
||||
from django.db.utils import OperationalError, ProgrammingError
|
||||
from django.utils.functional import lazy
|
||||
|
||||
try:
|
||||
from etpgrf.typograph import Typographer
|
||||
@@ -18,6 +25,123 @@ except ImportError:
|
||||
def __init__(self, **kwargs): pass
|
||||
|
||||
|
||||
class TagSelect2Widget(Select2TagWidget):
|
||||
"""
|
||||
Select2-виджет для django-taggit, работающий по ИМЕНАМ тегов.
|
||||
|
||||
- подхватывает уже сохранённые теги;
|
||||
- показывает выпадающий список из существующих тегов;
|
||||
- даёт создавать новые теги с пробелами в названии.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Определяем функцию, которая будет выполнена лениво.
|
||||
def get_choices_safely():
|
||||
try:
|
||||
# list() материализует ленивый QuerySet, что важно для lazy()
|
||||
return list(Tag.objects.values_list('name', 'name'))
|
||||
except (OperationalError, ProgrammingError):
|
||||
# Если таблицы нет (например, при collectstatic), возвращаем пустой список.
|
||||
return []
|
||||
# lazy() не выполняет функцию сразу, а создает "обещание" (promise),
|
||||
lazy_safe_choices = lazy(get_choices_safely, list)
|
||||
# choices: список всех существующих тегов по имени.
|
||||
# Присваиваем ему ленивый объект.
|
||||
self.choices = lazy_safe_choices()
|
||||
|
||||
# @property
|
||||
# def choices(self):
|
||||
# # Этот код будет выполняться только тогда,
|
||||
# # когда Django реально запросит self.choices для отрисовки.
|
||||
# # К этому моменту приложение будет полностью готово.
|
||||
# try:
|
||||
# self.choices = [(t.name, t.name) for t in Tag.objects.all()]
|
||||
# except (OperationalError, ProgrammingError):
|
||||
# self.choices = []
|
||||
#
|
||||
# # Важно: Нам нужно установить setter, даже если он пустой,
|
||||
# # потому что родительский класс будет пытаться присвоить ему значение.
|
||||
# @choices.setter
|
||||
# def choices(self, value):
|
||||
# pass
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
"all": ("css/select2_taggit_admin.css",),
|
||||
}
|
||||
|
||||
def build_attrs(self, base_attrs, extra_attrs=None):
|
||||
"""
|
||||
Настраиваем Select2 так, чтобы пробел НЕ разделял тег
|
||||
на несколько частей (нужны теги с пробелами: «Сергей Курёхин»).
|
||||
Оставляем в разделителях только запятую.
|
||||
"""
|
||||
attrs = super().build_attrs(base_attrs, extra_attrs)
|
||||
# По умолчанию django-select2 ставит: [",", " "]
|
||||
# Нам нужен только разделитель-запятая.
|
||||
# Строка '[","]' — корректный JSON-массив из одного элемента.
|
||||
# Важно: сюда нужно класть СТРОКУ с JSON-массивом, а не python-список.
|
||||
# Иначе в HTML окажется "['[\", \"]', ...]" и Select2 будет вести себя непредсказуемо.
|
||||
attrs["data-token-separators"] = '[","]'
|
||||
return attrs
|
||||
|
||||
def format_value(self, value):
|
||||
"""
|
||||
Преобразуем значение из TaggableManager/TagField
|
||||
в список ИМЁН тегов, который ожидает Select2TagWidget.
|
||||
"""
|
||||
from django.db.models import QuerySet
|
||||
|
||||
if value is None:
|
||||
return []
|
||||
|
||||
# QuerySet или список Tag-объектов
|
||||
if isinstance(value, QuerySet):
|
||||
return [t.name for t in value]
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
names = []
|
||||
for v in value:
|
||||
if isinstance(v, Tag):
|
||||
names.append(v.name)
|
||||
else:
|
||||
names.append(str(v))
|
||||
return names
|
||||
|
||||
# Строка вида "tag1, tag2" — разбираем в список имён
|
||||
if isinstance(value, str):
|
||||
return parse_tags(value)
|
||||
|
||||
return super().format_value(value)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
"""
|
||||
Django-Select2 возвращает список значений (['Сергей Курёхин', 'Другой тег']).
|
||||
Taggit (TagField) ждёт ОДНУ строку, которую потом парсит в список тегов.
|
||||
Если отдать список, он превратится в строку `"['Сергей', 'Курёхин']"`,
|
||||
и распарсится в кривые теги — этого мы избегаем.
|
||||
"""
|
||||
values = super().value_from_datadict(data, files, name)
|
||||
if not values:
|
||||
return ""
|
||||
|
||||
# Для нашего виджета value — это уже список имён тегов
|
||||
tag_names = [str(v).strip() for v in values if str(v).strip()]
|
||||
if not tag_names:
|
||||
return ""
|
||||
|
||||
# ОДИН многословный тег: "Сергей Курёхин" -> "Сергей Курёхин,"
|
||||
# Тогда parse_tags переключится в режим "деление по запятым"
|
||||
if len(tag_names) == 1:
|
||||
single = tag_names[0]
|
||||
if " " in single and "," not in single and '"' not in single:
|
||||
return single + ","
|
||||
return single
|
||||
|
||||
# Несколько тегов — явная запятая между ними.
|
||||
return ", ".join(tag_names)
|
||||
|
||||
|
||||
class DictumAdminForm(forms.ModelForm):
|
||||
# Виртуальные поля для настройки типографа
|
||||
etp_language = forms.ChoiceField(
|
||||
@@ -34,7 +158,7 @@ class DictumAdminForm(forms.ModelForm):
|
||||
)
|
||||
etp_hanging_punctuation = forms.ChoiceField(
|
||||
label="Висячая пунктуация",
|
||||
choices=[('no', 'Нет'), ('left', 'Слева'), ('right', 'Справа'), ('both', 'Обе стороны')],
|
||||
choices=[('no', 'Нет'), ('left', 'Слева'), ('right', 'Справа'), ],
|
||||
initial='left',
|
||||
required=False,
|
||||
help_text="Выносить кавычки за границу текстового блока"
|
||||
@@ -43,7 +167,7 @@ class DictumAdminForm(forms.ModelForm):
|
||||
label="Переносы",
|
||||
initial=True,
|
||||
required=False,
|
||||
help_text="Расставлять мягкие переносы (&shy;)"
|
||||
help_text="Расставлять мягкие переносы (<tt>&shy;</tt>)"
|
||||
)
|
||||
etp_sanitize = forms.BooleanField(
|
||||
label="Санитайзер (HTML)",
|
||||
@@ -62,6 +186,9 @@ class DictumAdminForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TbDictumAndQuotes
|
||||
fields = '__all__'
|
||||
widgets = {
|
||||
'tags': TagSelect2Widget,
|
||||
}
|
||||
|
||||
|
||||
# Register your models here.
|
||||
@@ -83,6 +210,7 @@ class AdmDictumAndQuotesAdmin(admin.ModelAdmin):
|
||||
('Настройки типографа (Etpgrf)', {
|
||||
'classes': ('collapse',),
|
||||
'fields': (
|
||||
('bTypograph',),
|
||||
('etp_language', 'etp_mode'),
|
||||
('etp_quotes', 'etp_sanitize'),
|
||||
('etp_hyphenation', 'etp_hanging_punctuation'),
|
||||
@@ -95,11 +223,15 @@ class AdmDictumAndQuotesAdmin(admin.ModelAdmin):
|
||||
}),
|
||||
('Служебное', {
|
||||
'classes': ('collapse',),
|
||||
'fields': ('iViewCounter', 'imFileOG', 'bTypograph') # bTypograph kept for compatibility
|
||||
'fields': ('iViewCounter', 'imFileOG', ) # bTypograph kept for compatibility
|
||||
})
|
||||
)
|
||||
readonly_fields = ('szIntroHTML', 'szContentHTML', 'iViewCounter')
|
||||
|
||||
formfield_overrides = {
|
||||
models.ManyToManyField: {'widget': Select2TagWidget},
|
||||
}
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
# 1. Читаем базовые настройки
|
||||
langs = form.cleaned_data.get('etp_language', 'ru').split(',')
|
||||
@@ -199,6 +331,11 @@ class AdmImages(admin.ModelAdmin):
|
||||
list_display_links = ('id', 'szCaption')
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
|
||||
# Добавляем виджет для тегов
|
||||
formfield_overrides = {
|
||||
TaggableManager: {'widget': TagSelect2Widget},
|
||||
}
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).prefetch_related('tags')
|
||||
|
||||
@@ -212,6 +349,11 @@ class AdmAuthor(admin.ModelAdmin):
|
||||
list_display_links = ('id', 'szAuthor')
|
||||
empty_value_display = u"<b style='color:red;'>-empty-</b>"
|
||||
|
||||
# Добавляем виджет для тегов
|
||||
formfield_overrides = {
|
||||
TaggableManager: {'widget': TagSelect2Widget},
|
||||
}
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).prefetch_related('tags')
|
||||
|
||||
@@ -223,4 +365,3 @@ admin.site.register(TbDictumAndQuotes, AdmDictumAndQuotesAdmin)
|
||||
admin.site.register(TbOrigin, AdmOrigin)
|
||||
admin.site.register(TbImages, AdmImages)
|
||||
admin.site.register(TbAuthor, AdmAuthor)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 6.0.2 on 2026-03-19 20:34
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web', '0003_alter_tbdictumandquotes_btypograph_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tbdictumandquotes',
|
||||
options={'ordering': ['-id'], 'verbose_name': 'ВЫСКАЗЫВАНИЕ', 'verbose_name_plural': 'ВЫСКАЗЫВАНИЯ'},
|
||||
),
|
||||
]
|
||||
@@ -16,6 +16,7 @@ import pytils
|
||||
class RuTag(Tag):
|
||||
class Meta:
|
||||
proxy = True
|
||||
# ordering = ['id']
|
||||
|
||||
def slugify(self, tag, i=None):
|
||||
return pytils.translit.slugify(self.name.lower())[:128]
|
||||
@@ -24,6 +25,7 @@ class RuTag(Tag):
|
||||
class RuTaggedItem(TaggedItem):
|
||||
class Meta:
|
||||
proxy = True
|
||||
# ordering = ['id']
|
||||
|
||||
@classmethod
|
||||
def tag_model(cls):
|
||||
@@ -108,7 +110,73 @@ class TbImages(models.Model):
|
||||
|
||||
# заменим имя файла картинки
|
||||
def save(self, *args, **kwargs):
|
||||
self.imFile.name = pytils.translit.slugify(self.szCaption.lower()) + str(Path(self.imFile.name).suffixes)
|
||||
import os
|
||||
from django.conf import settings
|
||||
|
||||
old_obj = None
|
||||
old_file_path = None
|
||||
|
||||
# Получаем старую запись, если она есть
|
||||
if self.pk:
|
||||
try:
|
||||
old_obj = TbImages.objects.get(pk=self.pk)
|
||||
# Пытаемся получить путь к файлу. Если файл не найден физически, Django может выкинуть ошибку здесь или позже
|
||||
# Поэтому просто берем имя из БД и формируем путь руками, чтобы не зависеть от Storage
|
||||
if old_obj.imFile:
|
||||
old_file_path = os.path.join(settings.MEDIA_ROOT, str(old_obj.imFile.name))
|
||||
except TbImages.DoesNotExist:
|
||||
pass
|
||||
|
||||
# Fix 1: Если старый путь уже битый (содержит ['...'])
|
||||
if old_file_path and "['" in old_file_path:
|
||||
# Формируем "исправленный" путь (каким он должен быть)
|
||||
corrected_path = old_file_path.replace("['", "").replace("']", "").replace("'", "")
|
||||
|
||||
# Проверяем: если битого файла нет, а исправленный есть -> значит БД врет
|
||||
if not os.path.exists(old_file_path) and os.path.exists(corrected_path):
|
||||
# Исправляем текущее имя файла в объекте (убираем мусор из имени)
|
||||
self.imFile.name = str(self.imFile.name).replace("['", "").replace("']", "").replace("'", "")
|
||||
# Обновляем переменную old_file_path, чтобы дальнейшая логика переименования работала корректно
|
||||
old_file_path = corrected_path
|
||||
|
||||
# Получаем текущее имя и расширение (уже возможно исправленное выше)
|
||||
current_path = Path(str(self.imFile.name))
|
||||
current_suffix = current_path.suffix
|
||||
|
||||
# Fix 2: Чиним расширение еще раз (на всякий случай, если Fix 1 не сработал или это новый объект)
|
||||
if "['" in str(current_suffix):
|
||||
current_suffix = str(current_suffix).replace("['", "").replace("']", "").replace("'", "")
|
||||
|
||||
# Формируем новое имя файла на основе заголовка (Slug)
|
||||
new_filename = pytils.translit.slugify(self.szCaption.lower()) + current_suffix
|
||||
|
||||
# Определяем папку (если есть родитель, используем его, иначе img2)
|
||||
# Важно: self.imFile.name может содержать полный путь. Нам нужен только относительный от MEDIA_ROOT
|
||||
# Но проще взять родителя из текущего имени
|
||||
parent_dir = current_path.parent.name if current_path.parent.name else 'img2'
|
||||
new_name_with_path = str(Path(parent_dir) / new_filename)
|
||||
|
||||
# Переименование физического файла
|
||||
# Сравниваем старое имя (из БД) с новым (сгенерированным)
|
||||
if old_obj and str(old_obj.imFile.name) != new_name_with_path:
|
||||
new_file_full_path = os.path.join(settings.MEDIA_ROOT, new_name_with_path)
|
||||
|
||||
# Если старый файл (old_file_path) существует физически, переименовываем его
|
||||
if old_file_path and os.path.exists(old_file_path):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(new_file_full_path), exist_ok=True)
|
||||
os.rename(old_file_path, new_file_full_path)
|
||||
self.imFile.name = new_name_with_path
|
||||
except OSError as e:
|
||||
print(f"Error renaming file from {old_file_path} to {new_file_full_path}: {e}")
|
||||
else:
|
||||
# Если старого файла нет, просто обновляем имя в БД
|
||||
self.imFile.name = new_name_with_path
|
||||
else:
|
||||
# Если имя не менялось или объекта не было, просто устанавливаем правильное имя
|
||||
# (например, чтобы убрать мусор из расширения в БД)
|
||||
self.imFile.name = new_name_with_path
|
||||
|
||||
super(TbImages, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -79,6 +79,8 @@ class CommonContextMixin:
|
||||
request.session['seen_ids'] = seen_ids
|
||||
|
||||
context.update({'DQ': dq})
|
||||
# slag текущей цитаты
|
||||
context.update({"DQ_SLUG": pytils.translit.slugify(dq.szContent.lower()[:120])})
|
||||
|
||||
# --- 3. АВТОР И ТЕГИ ---
|
||||
try:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
% cat docker-compose.yaml
|
||||
# ==============================================================================
|
||||
# Docker Compose для PRODUCTION
|
||||
# Этот файл запускается на боевом сервере.
|
||||
@@ -5,7 +6,8 @@
|
||||
# Вариант 2 (если оставили имя): docker compose -f docker-compose.prod.yml up -d
|
||||
# ==============================================================================
|
||||
|
||||
version: '3.8'
|
||||
# В новой версии Docker не нужно
|
||||
# version: '3.8'
|
||||
|
||||
services:
|
||||
# --- ОCНОВНОЙ СЕРВИС: DJANGO + GUNICORN + WHITENOISE ---
|
||||
@@ -27,25 +29,26 @@ services:
|
||||
|
||||
# 3. КОМАНДА ЗАПУСКА (Замена entrypoint.sh)
|
||||
# Выполняем цепочку команд внутри контейнера при запуске:
|
||||
# 1. Миграции
|
||||
# 2. Collectstatic
|
||||
# 3. Создаем папку nginx в примонтированном томе конфигов (если нет)
|
||||
# 4. Копирование конфига Nginx с авто-заменой путей через sed (замену реального пути на хосте получаем через переменную окружения HOST_PROJECT_PATH)
|
||||
# 5. Инициализация боевого конфига (если нет)
|
||||
# 6. Создаем папку для ошибок и копируем туда статические страницы 404/500
|
||||
# 7. Запуск Gunicorn
|
||||
# a. Миграции
|
||||
# b. Collectstatic
|
||||
# с. Создаем папку nginx в примонтированном томе конфигов (если нет)
|
||||
# d. Копирование конфига Nginx с авто-заменой путей через sed (замену реального пути на хосте получаем
|
||||
# через переменную окружения HOST_PROJECT_PATH)
|
||||
# e. Инициализация боевого конфига (если нет)
|
||||
# f. Создаем папку для ошибок и копируем туда статические страницы 404/500 (там их увидит Nginx хоста)
|
||||
# g. Запуск Gunicorn
|
||||
command: >
|
||||
sh -c "python manage.py migrate --noinput &&
|
||||
python manage.py collectstatic --noinput &&
|
||||
mkdir -p /nginx_configs_host/nginx &&
|
||||
sed \"s|/home/user/app/dq-site|${HOST_PROJECT_PATH:-/home/default_user/projects/dq-site}|g\" /app/configs/nginx/dq-app--external-nginx.conf > /nginx_configs_host/nginx/nginx_dq.conf.example &&
|
||||
sed \"s|/home/user/app/dq-site|${HOST_PROJECT_PATH:-/home/default_user/projects/dq-site}|g\" /nginx_configs_host/nginx/dq-app--external-nginx.conf > /nginx_configs_host/nginx/nginx_dq.conf.example &&
|
||||
if [ ! -f /nginx_configs_host/nginx/dq-app--external-nginx.conf ]; then
|
||||
cp /nginx_configs_host/nginx/nginx_dq.conf.example /nginx_configs_host/nginx/dq-app--external-nginx.conf;
|
||||
echo 'INIT: Created new nginx config with correct paths';
|
||||
fi &&
|
||||
mkdir -p /app/public/media/errors &&
|
||||
cp /app/dicquo/templates/static_404.html /app/public/media/errors/404.html &&
|
||||
cp /app/dicquo/templates/static_500.html /app/public/media/errors/500.html &&
|
||||
cp /home/app/web/dicquo/templates/static_404.html /app/public/media/errors/404.html &&
|
||||
cp /home/app/web/dicquo/templates/static_500.html /app/public/media/errors/500.html &&
|
||||
gunicorn --workers 3 --bind 0.0.0.0:8000 dicquo.wsgi:application"
|
||||
|
||||
# 4. Проброс портов (Внешний Nginx -> localhost:8010)
|
||||
@@ -57,17 +60,24 @@ services:
|
||||
volumes:
|
||||
# База данных
|
||||
# Монтируем папку database с хоста в папку с базой внутри контейнера.
|
||||
# Путь в контейнере: /app/database (так как Django ищет базу в BASE_DIR.parent/database)
|
||||
- ./database:/app/database
|
||||
# Путь в контейнере: /home/app/web/database (так как Django ищет базу в BASE_DIR.parent/database)
|
||||
- ./database:/home/app/web/database
|
||||
|
||||
# Медиа (папка media должна быть рядом с docker-compose.yml)
|
||||
- ./media:/app/public/media
|
||||
- ./media:/home/app/web/public/media
|
||||
|
||||
# Конфиги (Монтируем папку ./config с хоста в /nginx_configs_host внутри контейнера)
|
||||
# Это нужно, чтобы скрипт запуска мог положить туда .example конфиг и прочитать боевой конфиг.
|
||||
- ./config:/nginx_configs_host
|
||||
|
||||
# 6. Переменные окружения
|
||||
# 6. Пользователь и права
|
||||
user: "1000:1000"
|
||||
|
||||
# Когда нужна отладка процессов внутри контейнера, можно временно раскомментировать эту строку и запустить контейнер с правами root.
|
||||
# cap_add:
|
||||
# - SYS_PTRACE
|
||||
|
||||
# 7. Переменные окружения
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
@@ -76,14 +86,24 @@ services:
|
||||
# Передаем переменную с путем на хосте внутрь контейнера, чтобы sed мог её использовать
|
||||
- HOST_PROJECT_PATH=${HOST_PROJECT_PATH:-/home/default_user/projects/dq-site}
|
||||
|
||||
# 7. Логирование (Ротация)
|
||||
# 8. Проверка здоровья контейнера (Healthcheck)
|
||||
# Docker будет периодически проверять статус контейнера. Это критично для Watchtower!
|
||||
# Если контейнер объявлен "unhealthy", Watchtower сначала остановит старый образ, потом запустит новый.
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()"]
|
||||
interval: 30s # Проверка каждые 30 секунд
|
||||
timeout: 3s # Таймаут ответа - 3 секунды
|
||||
start_period: 10s # Даем 10 секунд на стартап перед первой проверкой
|
||||
retries: 3 # Unhealthy после 3 неудачных попыток
|
||||
|
||||
# 9. Логирование (Ротация)
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
# 8. Ресурсы
|
||||
# 10. Ресурсы
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
@@ -106,10 +126,13 @@ services:
|
||||
- REPO_PASS=${REPO_PASS}
|
||||
- WATCHTOWER_SCOPE=dq-scope
|
||||
- WATCHTOWER_CLEANUP=true # Удалять старые образы после обновления
|
||||
- WATCHTOWER_POLL_INTERVAL=1800 # Проверять каждые 30 минут
|
||||
- DOCKER_API_VERSION=1.44
|
||||
# Дополнительные опции для правильной работы с healthcheck
|
||||
- WATCHTOWER_WAIT_ON_TIMEOUT=60 # Ждем 60 сек пока контейнер станет healthy перед финализацией
|
||||
- WATCHTOWER_LIFECYCLE_HOOKS=true # Включаем lifecycle hooks для graceful shutdown
|
||||
command: --interval 1800 --cleanup # Проверять каждые 30 минут
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
max-file: "3"
|
||||
310
poetry.lock
generated
310
poetry.lock
generated
@@ -49,13 +49,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "6.0.2"
|
||||
version = "6.0.3"
|
||||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
||||
optional = false
|
||||
python-versions = ">=3.12"
|
||||
files = [
|
||||
{file = "django-6.0.2-py3-none-any.whl", hash = "sha256:610dd3b13d15ec3f1e1d257caedd751db8033c5ad8ea0e2d1219a8acf446ecc6"},
|
||||
{file = "django-6.0.2.tar.gz", hash = "sha256:3046a53b0e40d4b676c3b774c73411d7184ae2745fe8ce5e45c0f33d3ddb71a7"},
|
||||
{file = "django-6.0.3-py3-none-any.whl", hash = "sha256:2e5974441491ddb34c3f13d5e7a9f97b07ba03bf70234c0a9c68b79bbb235bc3"},
|
||||
{file = "django-6.0.3.tar.gz", hash = "sha256:90be765ee756af8a6cbd6693e56452404b5ad15294f4d5e40c0a55a0f4870fe1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -67,22 +67,51 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
argon2 = ["argon2-cffi (>=23.1.0)"]
|
||||
bcrypt = ["bcrypt (>=4.1.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "django-appconf"
|
||||
version = "1.2.0"
|
||||
description = "A helper class for handling configuration defaults of packaged apps gracefully."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "django_appconf-1.2.0-py3-none-any.whl", hash = "sha256:b81bce5ef0ceb9d84df48dfb623a32235d941c78cc5e45dbb6947f154ea277f4"},
|
||||
{file = "django_appconf-1.2.0.tar.gz", hash = "sha256:15a88d60dd942d6059f467412fe4581db632ef03018a3c183fb415d6fc9e5cec"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
django = "*"
|
||||
|
||||
[[package]]
|
||||
name = "django-environ"
|
||||
version = "0.12.1"
|
||||
version = "0.13.0"
|
||||
description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application."
|
||||
optional = false
|
||||
python-versions = "<4,>=3.9"
|
||||
files = [
|
||||
{file = "django_environ-0.12.1-py2.py3-none-any.whl", hash = "sha256:064ba2d5082f833e6d7fe4def4928bde1eedc0248a417575da7db147aeec1c20"},
|
||||
{file = "django_environ-0.12.1.tar.gz", hash = "sha256:22859c6e905ab7637fa3348d1787543bb4492f38d761104a3ce0519b7b752845"},
|
||||
{file = "django_environ-0.13.0-py3-none-any.whl", hash = "sha256:37799d14cd78222c6fd8298e48bfe17965ff8e586091ad66a463e52e0e7b799e"},
|
||||
{file = "django_environ-0.13.0.tar.gz", hash = "sha256:6c401e4c219442c2c4588c2116d5292b5484a6f69163ed09cd41f3943bfb645f"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
develop = ["coverage[toml] (>=5.0a4)", "furo (>=2024.8.6)", "pytest (>=4.6.11)", "setuptools (>=71.0.0)", "sphinx (>=5.0)", "sphinx-notfound-page"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=5.0)", "sphinx-notfound-page"]
|
||||
develop = ["coverage[toml] (>=5.0a4)", "furo (>=2024.8.6)", "pytest (>=4.6.11)", "setuptools (>=71.0.0)", "sphinx (>=5.0)", "sphinx-copybutton", "sphinx-notfound-page"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=5.0)", "sphinx-copybutton", "sphinx-notfound-page"]
|
||||
testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)", "setuptools (>=71.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "django-select2"
|
||||
version = "8.4.8"
|
||||
description = "This is a Django_ integration of Select2_."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "django_select2-8.4.8-py3-none-any.whl", hash = "sha256:a2ce6a4c556dd2d4d57eb3753618d6f31f8d3910e9d9fa1b686d9340f50b14eb"},
|
||||
{file = "django_select2-8.4.8.tar.gz", hash = "sha256:592e52effff2b5850cb7c98b265715b6704fb784699c4aedddfdd8ae1ffa1e81"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
django = ">=4.2"
|
||||
django-appconf = ">=0.6.0"
|
||||
|
||||
[[package]]
|
||||
name = "django-taggit"
|
||||
version = "6.1.0"
|
||||
@@ -99,13 +128,13 @@ Django = ">=4.1"
|
||||
|
||||
[[package]]
|
||||
name = "etpgrf"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6.post1"
|
||||
description = "Electro-Typographer: Python library for advanced web typography (non-breaking spaces, hyphenation, hanging punctuation and ."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "etpgrf-0.1.4-py3-none-any.whl", hash = "sha256:62d4371e1b5fab06b99f79bd351767aed8baf7d041cae7e5d4eb63f7c9545114"},
|
||||
{file = "etpgrf-0.1.4.tar.gz", hash = "sha256:c699382c292e3110915331dd5539e7dde0c961e4f4ca65cf8db0e01e84dab72f"},
|
||||
{file = "etpgrf-0.1.6.post1-py3-none-any.whl", hash = "sha256:0863b14385bdacdd405f137ca2ce6bdb6f683f0189e8c927196a1eee754366be"},
|
||||
{file = "etpgrf-0.1.6.post1.tar.gz", hash = "sha256:984d201cff232a58c05b6f4455a50f822162520df829ad4d543bfe0b7fd962a9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -422,142 +451,125 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2026.1.15"
|
||||
version = "2026.2.28"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "regex-2026.1.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4e3dd93c8f9abe8aa4b6c652016da9a3afa190df5ad822907efe6b206c09896e"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97499ff7862e868b1977107873dd1a06e151467129159a6ffd07b66706ba3a9f"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bda75ebcac38d884240914c6c43d8ab5fb82e74cde6da94b43b17c411aa4c2b"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7dcc02368585334f5bc81fc73a2a6a0bbade60e7d83da21cead622faf408f32c"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:693b465171707bbe882a7a05de5e866f33c76aa449750bee94a8d90463533cc9"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b0d190e6f013ea938623a58706d1469a62103fb2a241ce2873a9906e0386582c"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ff818702440a5878a81886f127b80127f5d50563753a28211482867f8318106"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f052d1be37ef35a54e394de66136e30fa1191fab64f71fc06ac7bc98c9a84618"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6bfc31a37fd1592f0c4fc4bfc674b5c42e52efe45b4b7a6a14f334cca4bcebe4"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d6ce5ae80066b319ae3bc62fd55a557c9491baa5efd0d355f0de08c4ba54e79"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1704d204bd42b6bb80167df0e4554f35c255b579ba99616def38f69e14a5ccb9"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e3174a5ed4171570dc8318afada56373aa9289eb6dc0d96cceb48e7358b0e220"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:87adf5bd6d72e3e17c9cb59ac4096b1faaf84b7eb3037a5ffa61c4b4370f0f13"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e85dc94595f4d766bd7d872a9de5ede1ca8d3063f3bdf1e2c725f5eb411159e3"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-win32.whl", hash = "sha256:21ca32c28c30d5d65fc9886ff576fc9b59bbca08933e844fa2363e530f4c8218"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-win_amd64.whl", hash = "sha256:3038a62fc7d6e5547b8915a3d927a0fbeef84cdbe0b1deb8c99bbd4a8961b52a"},
|
||||
{file = "regex-2026.1.15-cp310-cp310-win_arm64.whl", hash = "sha256:505831646c945e3e63552cc1b1b9b514f0e93232972a2d5bedbcc32f15bc82e3"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ae6020fb311f68d753b7efa9d4b9a5d47a5d6466ea0d5e3b5a471a960ea6e4a"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eddf73f41225942c1f994914742afa53dc0d01a6e20fe14b878a1b1edc74151f"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e8cd52557603f5c66a548f69421310886b28b7066853089e1a71ee710e1cdc1"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5170907244b14303edc5978f522f16c974f32d3aa92109fabc2af52411c9433b"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2748c1ec0663580b4510bd89941a31560b4b439a0b428b49472a3d9944d11cd8"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2f2775843ca49360508d080eaa87f94fa248e2c946bbcd963bb3aae14f333413"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9ea2604370efc9a174c1b5dcc81784fb040044232150f7f33756049edfc9026"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcd31594264029b57bf16f37fd7248a70b3b764ed9e0839a8f271b2d22c0785"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c08c1f3e34338256732bd6938747daa3c0d5b251e04b6e43b5813e94d503076e"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e43a55f378df1e7a4fa3547c88d9a5a9b7113f653a66821bcea4718fe6c58763"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f82110ab962a541737bd0ce87978d4c658f06e7591ba899192e2712a517badbb"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:27618391db7bdaf87ac6c92b31e8f0dfb83a9de0075855152b720140bda177a2"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bfb0d6be01fbae8d6655c8ca21b3b72458606c4aec9bbc932db758d47aba6db1"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-win32.whl", hash = "sha256:b10e42a6de0e32559a92f2f8dc908478cc0fa02838d7dbe764c44dca3fa13569"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-win_amd64.whl", hash = "sha256:e9bf3f0bbdb56633c07d7116ae60a576f846efdd86a8848f8d62b749e1209ca7"},
|
||||
{file = "regex-2026.1.15-cp311-cp311-win_arm64.whl", hash = "sha256:41aef6f953283291c4e4e6850607bd71502be67779586a61472beacb315c97ec"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a"},
|
||||
{file = "regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e"},
|
||||
{file = "regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10"},
|
||||
{file = "regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903"},
|
||||
{file = "regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf"},
|
||||
{file = "regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:55b4ea996a8e4458dd7b584a2f89863b1655dd3d17b88b46cbb9becc495a0ec5"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e1e28be779884189cdd57735e997f282b64fd7ccf6e2eef3e16e57d7a34a815"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0057de9eaef45783ff69fa94ae9f0fd906d629d0bd4c3217048f46d1daa32e9b"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc7cd0b2be0f0269283a45c0d8b2c35e149d1319dcb4a43c9c3689fa935c1ee6"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8db052bbd981e1666f09e957f3790ed74080c2229007c1dd67afdbf0b469c48b"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:343db82cb3712c31ddf720f097ef17c11dab2f67f7a3e7be976c4f82eba4e6df"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:55e9d0118d97794367309635df398bdfd7c33b93e2fdfa0b239661cd74b4c14e"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:008b185f235acd1e53787333e5690082e4f156c44c87d894f880056089e9bc7c"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fd65af65e2aaf9474e468f9e571bd7b189e1df3a61caa59dcbabd0000e4ea839"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f42e68301ff4afee63e365a5fc302b81bb8ba31af625a671d7acb19d10168a8c"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f7792f27d3ee6e0244ea4697d92b825f9a329ab5230a78c1a68bd274e64b5077"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:dbaf3c3c37ef190439981648ccbf0c02ed99ae066087dd117fcb616d80b010a4"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:adc97a9077c2696501443d8ad3fa1b4fc6d131fc8fd7dfefd1a723f89071cf0a"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:069f56a7bf71d286a6ff932a9e6fb878f151c998ebb2519a9f6d1cee4bffdba3"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-win32.whl", hash = "sha256:ea4e6b3566127fda5e007e90a8fd5a4169f0cf0619506ed426db647f19c8454a"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-win_amd64.whl", hash = "sha256:cda1ed70d2b264952e88adaa52eea653a33a1b98ac907ae2f86508eb44f65cdc"},
|
||||
{file = "regex-2026.1.15-cp39-cp39-win_arm64.whl", hash = "sha256:b325d4714c3c48277bfea1accd94e193ad6ed42b4bad79ad64f3b8f8a31260a5"},
|
||||
{file = "regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fc48c500838be6882b32748f60a15229d2dea96e59ef341eaa96ec83538f498d"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2afa673660928d0b63d84353c6c08a8a476ddfc4a47e11742949d182e6863ce8"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7ab218076eb0944549e7fe74cf0e2b83a82edb27e81cc87411f76240865e04d5"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d63db12e45a9b9f064bfe4800cefefc7e5f182052e4c1b774d46a40ab1d9bb"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:195237dc327858a7721bf8b0bbbef797554bc13563c3591e91cd0767bacbe359"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b387a0d092dac157fb026d737dde35ff3e49ef27f285343e7c6401851239df27"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3935174fa4d9f70525a4367aaff3cb8bc0548129d114260c29d9dfa4a5b41692"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b2b23587b26496ff5fd40df4278becdf386813ec00dc3533fa43a4cf0e2ad3c"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3b24bd7e9d85dc7c6a8bd2aa14ecd234274a0248335a02adeb25448aecdd420d"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd477d5f79920338107f04aa645f094032d9e3030cc55be581df3d1ef61aa318"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b49eb78048c6354f49e91e4b77da21257fecb92256b6d599ae44403cab30b05b"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a25c7701e4f7a70021db9aaf4a4a0a67033c6318752146e03d1b94d32006217e"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9dd450db6458387167e033cfa80887a34c99c81d26da1bf8b0b41bf8c9cac88e"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2954379dd20752e82d22accf3ff465311cbb2bac6c1f92c4afd400e1757f7451"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-win32.whl", hash = "sha256:1f8b17be5c27a684ea6759983c13506bd77bfc7c0347dff41b18ce5ddd2ee09a"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-win_amd64.whl", hash = "sha256:dd8847c4978bc3c7e6c826fb745f5570e518b8459ac2892151ce6627c7bc00d5"},
|
||||
{file = "regex-2026.2.28-cp310-cp310-win_arm64.whl", hash = "sha256:73cdcdbba8028167ea81490c7f45280113e41db2c7afb65a276f4711fa3bcbff"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e621fb7c8dc147419b28e1702f58a0177ff8308a76fa295c71f3e7827849f5d9"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d5bef2031cbf38757a0b0bc4298bb4824b6332d28edc16b39247228fbdbad97"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bcb399ed84eabf4282587ba151f2732ad8168e66f1d3f85b1d038868fe547703"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c1b34dfa72f826f535b20712afa9bb3ba580020e834f3c69866c5bddbf10098"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:851fa70df44325e1e4cdb79c5e676e91a78147b1b543db2aec8734d2add30ec2"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:516604edd17b1c2c3e579cf4e9b25a53bf8fa6e7cedddf1127804d3e0140ca64"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7ce83654d1ab701cb619285a18a8e5a889c1216d746ddc710c914ca5fd71022"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2791948f7c70bb9335a9102df45e93d428f4b8128020d85920223925d73b9e1"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03a83cc26aa2acda6b8b9dfe748cf9e84cbd390c424a1de34fdcef58961a297a"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ec6f5674c5dc836994f50f1186dd1fafde4be0666aae201ae2fcc3d29d8adf27"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:50c2fc924749543e0eacc93ada6aeeb3ea5f6715825624baa0dccaec771668ae"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ba55c50f408fb5c346a3a02d2ce0ebc839784e24f7c9684fde328ff063c3cdea"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:edb1b1b3a5576c56f08ac46f108c40333f222ebfd5cf63afdfa3aab0791ebe5b"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-win32.whl", hash = "sha256:948c12ef30ecedb128903c2c2678b339746eb7c689c5c21957c4a23950c96d15"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-win_amd64.whl", hash = "sha256:fd63453f10d29097cc3dc62d070746523973fb5aa1c66d25f8558bebd47fed61"},
|
||||
{file = "regex-2026.2.28-cp311-cp311-win_arm64.whl", hash = "sha256:00f2b8d9615aa165fdff0a13f1a92049bfad555ee91e20d246a51aa0b556c60a"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fcf26c3c6d0da98fada8ae4ef0aa1c3405a431c0a77eb17306d38a89b02adcd7"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02473c954af35dd2defeb07e44182f5705b30ea3f351a7cbffa9177beb14da5d"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b65d33a17101569f86d9c5966a8b1d7fbf8afdda5a8aa219301b0a80f58cf7d"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71dcecaa113eebcc96622c17692672c2d104b1d71ddf7adeda90da7ddeb26fc"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:481df4623fa4969c8b11f3433ed7d5e3dc9cec0f008356c3212b3933fb77e3d8"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64e7c6ad614573e0640f271e811a408d79a9e1fe62a46adb602f598df42a818d"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6b08a06976ff4fb0d83077022fde3eca06c55432bb997d8c0495b9a4e9872f4"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:864cdd1a2ef5716b0ab468af40139e62ede1b3a53386b375ec0786bb6783fc05"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:511f7419f7afab475fd4d639d4aedfc54205bcb0800066753ef68a59f0f330b5"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b42f7466e32bf15a961cf09f35fa6323cc72e64d3d2c990b10de1274a5da0a59"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8710d61737b0c0ce6836b1da7109f20d495e49b3809f30e27e9560be67a257bf"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4390c365fd2d45278f45afd4673cb90f7285f5701607e3ad4274df08e36140ae"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cb3b1db8ff6c7b8bf838ab05583ea15230cb2f678e569ab0e3a24d1e8320940b"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-win32.whl", hash = "sha256:f8ed9a5d4612df9d4de15878f0bc6aa7a268afbe5af21a3fdd97fa19516e978c"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-win_amd64.whl", hash = "sha256:01d65fd24206c8e1e97e2e31b286c59009636c022eb5d003f52760b0f42155d4"},
|
||||
{file = "regex-2026.2.28-cp312-cp312-win_arm64.whl", hash = "sha256:c0b5ccbb8ffb433939d248707d4a8b31993cb76ab1a0187ca886bf50e96df952"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6d63a07e5ec8ce7184452cb00c41c37b49e67dc4f73b2955b5b8e782ea970784"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e59bc8f30414d283ae8ee1617b13d8112e7135cb92830f0ec3688cb29152585a"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:de0cf053139f96219ccfabb4a8dd2d217c8c82cb206c91d9f109f3f552d6b43d"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb4db2f17e6484904f986c5a657cec85574c76b5c5e61c7aae9ffa1bc6224f95"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52b017b35ac2214d0db5f4f90e303634dc44e4aba4bd6235a27f97ecbe5b0472"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69fc560ccbf08a09dc9b52ab69cacfae51e0ed80dc5693078bdc97db2f91ae96"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e61eea47230eba62a31f3e8a0e3164d0f37ef9f40529fb2c79361bc6b53d2a92"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4f5c0b182ad4269e7381b7c27fdb0408399881f7a92a4624fd5487f2971dfc11"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:96f6269a2882fbb0ee76967116b83679dc628e68eaea44e90884b8d53d833881"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5acd4b6a95f37c3c3828e5d053a7d4edaedb85de551db0153754924cb7c83e3"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2234059cfe33d9813a3677ef7667999caea9eeaa83fef98eb6ce15c6cf9e0215"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c15af43c72a7fb0c97cbc66fa36a43546eddc5c06a662b64a0cbf30d6ac40944"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9185cc63359862a6e80fe97f696e04b0ad9a11c4ac0a4a927f979f611bfe3768"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-win32.whl", hash = "sha256:fb66e5245db9652abd7196ace599b04d9c0e4aa7c8f0e2803938377835780081"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-win_amd64.whl", hash = "sha256:71a911098be38c859ceb3f9a9ce43f4ed9f4c6720ad8684a066ea246b76ad9ff"},
|
||||
{file = "regex-2026.2.28-cp313-cp313-win_arm64.whl", hash = "sha256:39bb5727650b9a0275c6a6690f9bb3fe693a7e6cc5c3155b1240aedf8926423e"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:97054c55db06ab020342cc0d35d6f62a465fa7662871190175f1ad6c655c028f"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d25a10811de831c2baa6aef3c0be91622f44dd8d31dd12e69f6398efb15e48b"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d6cfe798d8da41bb1862ed6e0cba14003d387c3c0c4a5d45591076ae9f0ce2f8"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd0ce43e71d825b7c0661f9c54d4d74bd97c56c3fd102a8985bcfea48236bacb"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00945d007fd74a9084d2ab79b695b595c6b7ba3698972fadd43e23230c6979c1"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bec23c11cbbf09a4df32fe50d57cbdd777bc442269b6e39a1775654f1c95dee2"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cdcc17d935c8f9d3f4db5c2ebe2640c332e3822ad5d23c2f8e0228e6947943a"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a448af01e3d8031c89c5d902040b124a5e921a25c4e5e07a861ca591ce429341"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:10d28e19bd4888e4abf43bd3925f3c134c52fdf7259219003588a42e24c2aa25"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:99985a2c277dcb9ccb63f937451af5d65177af1efdeb8173ac55b61095a0a05c"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:e1e7b24cb3ae9953a560c563045d1ba56ee4749fbd05cf21ba571069bd7be81b"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d8511a01d0e4ee1992eb3ba19e09bc1866fe03f05129c3aec3fdc4cbc77aad3f"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:aaffaecffcd2479ce87aa1e74076c221700b7c804e48e98e62500ee748f0f550"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-win32.whl", hash = "sha256:ef77bdde9c9eba3f7fa5b58084b29bbcc74bcf55fdbeaa67c102a35b5bd7e7cc"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-win_amd64.whl", hash = "sha256:98adf340100cbe6fbaf8e6dc75e28f2c191b1be50ffefe292fb0e6f6eefdb0d8"},
|
||||
{file = "regex-2026.2.28-cp313-cp313t-win_arm64.whl", hash = "sha256:2fb950ac1d88e6b6a9414381f403797b236f9fa17e1eee07683af72b1634207b"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-win32.whl", hash = "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-win_amd64.whl", hash = "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7"},
|
||||
{file = "regex-2026.2.28-cp314-cp314-win_arm64.whl", hash = "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-win32.whl", hash = "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-win_amd64.whl", hash = "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9"},
|
||||
{file = "regex-2026.2.28-cp314-cp314t-win_arm64.whl", hash = "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec"},
|
||||
{file = "regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -631,13 +643,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "whitenoise"
|
||||
version = "6.11.0"
|
||||
version = "6.12.0"
|
||||
description = "Radically simplified static file serving for WSGI applications"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "whitenoise-6.11.0-py3-none-any.whl", hash = "sha256:b2aeb45950597236f53b5342b3121c5de69c8da0109362aee506ce88e022d258"},
|
||||
{file = "whitenoise-6.11.0.tar.gz", hash = "sha256:0f5bfce6061ae6611cd9396a8231e088722e4fc67bc13a111be74c738d99375f"},
|
||||
{file = "whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2"},
|
||||
{file = "whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -646,4 +658,4 @@ brotli = ["brotli"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "3d7a7f2fe8ec78993616e707e29e96503f134bd1cec48cac7f6dd47814863f4f"
|
||||
content-hash = "fb71f5e7011418134e09152004d990412cf923433f675398e5e99439bf9653b9"
|
||||
|
||||
@@ -1,26 +1,14 @@
|
||||
# DicQuo
|
||||
User-Agent: *
|
||||
Allow: /
|
||||
Disallow: /admin/
|
||||
Disallow: /*?tag=
|
||||
Disallow: /*?
|
||||
|
||||
# Optimize for Yandex
|
||||
Clean-param: tag /
|
||||
|
||||
# AI and LLM bots settings
|
||||
# OpenAI GPT
|
||||
# User-agent: GPTBot
|
||||
# Disallow:
|
||||
|
||||
# Common Crawl (used by many AI models)
|
||||
# User-agent: CCBot
|
||||
# Disallow:
|
||||
|
||||
# Google Bard/Gemini
|
||||
# User-agent: Google-Extended
|
||||
# Disallow:
|
||||
|
||||
Host: dq.cube2.ru
|
||||
Sitemap: https://dq.cube2.ru/sitemap.xml
|
||||
|
||||
# Ссылка на файл для ИИ-моделей
|
||||
Link: /llms.txt
|
||||
@@ -1,8 +1,8 @@
|
||||
@charset "utf-8";
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
min-width: 100vw;
|
||||
min-height: 100vmin;
|
||||
min-width: 100vmin;
|
||||
background-color: #111; /* Изначально темный фон */
|
||||
opacity: 0; /* Скрываем контент до расчета цвета */
|
||||
transition: opacity 0.9s ease-in-out; /* Очень плавное появление */
|
||||
@@ -13,11 +13,11 @@ header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1vh 4vw;
|
||||
padding: 1vmin 4vmin;
|
||||
}
|
||||
|
||||
header > #logo {
|
||||
margin-top: 1vh;
|
||||
margin-top: 1vmin;
|
||||
float: left;
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ header > nav > #stats-menu > b {
|
||||
|
||||
header > nav > #stats-menu > p {
|
||||
font-style: italic; display: inline-block;
|
||||
margin: 0 1vw;
|
||||
padding-right: 1vw;
|
||||
margin: 0 1vmin;
|
||||
padding-right: 1vmin;
|
||||
border-right: 1px dotted silver;
|
||||
}
|
||||
|
||||
@@ -120,23 +120,18 @@ header > nav > #stats-menu > a:hover {
|
||||
|
||||
/* MAIN ARTICLE CONTENT */
|
||||
main {
|
||||
/*justify-content: space-between;*/
|
||||
/*align-items: center;*/
|
||||
padding: 1vh 8vw;
|
||||
padding: 1vmin 8vmin;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
/*width: 90%;*/
|
||||
/*max-width: 1200px;*/
|
||||
/*margin: 0 auto;*/
|
||||
min-height: 60vmin;
|
||||
}
|
||||
|
||||
main > article {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2vw;
|
||||
gap: 2vmin;
|
||||
}
|
||||
|
||||
main > article > figure {
|
||||
@@ -170,18 +165,18 @@ main > article > figure > cite { /* Автор цитаты */
|
||||
}
|
||||
|
||||
main > article > div {
|
||||
flex: 0 0 30vw;
|
||||
flex: 0 0 30vmax;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 30vw;
|
||||
width: 30vmax;
|
||||
text-align: right;
|
||||
margin-bottom: 10vh;
|
||||
margin-bottom: 10vmin;
|
||||
}
|
||||
|
||||
main > article > div > div {
|
||||
width: 26vmax;
|
||||
height: 26vmax;
|
||||
padding: 0.5vw;
|
||||
padding: 0.5vmin;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@@ -200,13 +195,13 @@ main > article > div > div > div > img {
|
||||
|
||||
/* НАВИГАЦИЯ (ТЕГИ И ДАЛЕЕ) В КОНЦЕ */
|
||||
nav {
|
||||
padding: 1vh 4vw;
|
||||
padding: 1vmin 4vmin;
|
||||
}
|
||||
nav > div {
|
||||
color: silver;
|
||||
font-size: 1.5vmin;
|
||||
line-height: 1.9vmin;
|
||||
padding-top: 7vh;
|
||||
padding: 7vmin 0 4vmin 0;
|
||||
}
|
||||
nav > div a {
|
||||
text-decoration: none;
|
||||
@@ -250,7 +245,7 @@ footer {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 2vh 4vw;
|
||||
padding: 2vmin 4vmin;
|
||||
color: silver; /* Мягкий серый цвет текста */
|
||||
background-color: rgba(30, 30, 30, 0.8); /* Темный полупрозрачный фон */
|
||||
backdrop-filter: blur(5px); /* Эффект матового стекла (современно и медитативно) */
|
||||
@@ -262,12 +257,12 @@ footer {
|
||||
|
||||
footer small {
|
||||
display: inline-block;
|
||||
margin-right: 2vw;
|
||||
margin-right: 2vmin;
|
||||
letter-spacing: 0.05em; /* Немного воздуха в тексте */
|
||||
}
|
||||
|
||||
footer button {
|
||||
padding: 0.5vh 1.5vw;
|
||||
padding: 0.5vmin 1.5vmin;
|
||||
background: transparent;
|
||||
color: silver;
|
||||
border: 1px solid silver;
|
||||
@@ -292,18 +287,36 @@ footer button:hover {
|
||||
|
||||
main > article > div {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: 2vh;
|
||||
margin: 8vmin 0 2vmin 0;
|
||||
}
|
||||
|
||||
main > article > div {
|
||||
width: 80vmin;
|
||||
}
|
||||
|
||||
main > article > div > div {
|
||||
width: 36vmax;
|
||||
height: 36vmax;
|
||||
}
|
||||
|
||||
main > article > div > div > div > img {
|
||||
height: 36vmax;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- ВЫРАВНИВАНИЕ СИМВОЛОВ ВИСЯЧЕЙ ПУНКТУАЦИИ (Hanging Punctuation) ТИПОГРАФА ETPGRF --- */
|
||||
/* --- В ПРОЕКТЕ ТОЛЬКО ЛЕВЫЕ ВИСЯЧИЕ СИМВОЛЫ (выравнивание по левому краю) --- */
|
||||
.etp-laquo {margin-left: -0.44em;} /* « */
|
||||
.etp-ldquo, .etp-bdquo { margin-left: -0.4em;} /* “ „ */
|
||||
.etp-lsquo {margin-left: -0.22em;} /* ‘ */
|
||||
.etp-lpar, .etp-lsqb, .etp-lcub {margin-left: -0.25em;}/* ( [ { */
|
||||
.etp-laquo { margin-left: -0.49em; } /* « */
|
||||
.etp-ldquo, .etp-bdquo { margin-left: -0.4em; } /* “ “ */
|
||||
.etp-lsquo { margin-left: -0.22em; } /* ’ */
|
||||
.etp-lpar, .etp-lsqb, .etp-lcub { margin-left: -0.23em; } /* ( [ { */
|
||||
/* компенсирующие пробелы для левых висячих символов */
|
||||
.etp-sp-laquo { padding-right: 0.49em; }
|
||||
.etp-sp-ldquo, .etp-sp-bdquo { padding-right: 0.4em; }
|
||||
.etp-sp-lsquo { padding-right: 0.22em; }
|
||||
.etp-sp-lpar, .etp-sp-lsqb, .etp-sp-lcub { padding-right: 0.23em; }
|
||||
|
||||
/* --- СЧЕТЧИКИ (СКРЫТЫЙ ПИКСЕЛЬ) --- */
|
||||
/* --- СЧЕТЧИКИ (СКРЫТЫЙ ПИКСЕЛЬ) top.mail.ru и Яндекс.Метрика --- */
|
||||
.counter-pixel {
|
||||
border: 0;
|
||||
position: absolute;
|
||||
|
||||
60
public/static/css/select2_taggit_admin.css
Normal file
60
public/static/css/select2_taggit_admin.css
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Select2 (django-select2) dark theme compatibility for Django Admin.
|
||||
We intentionally scope to dark mode only and lean on Django Admin CSS variables. */
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not([data-theme="light"]) .select2-container--default .select2-selection--single,
|
||||
html:not([data-theme="light"]) .select2-container--default .select2-selection--multiple {
|
||||
background: var(--body-bg, #1e1e1e) !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
border-color: var(--border-color, #3a3a3a) !important;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-selection--single,
|
||||
html[data-theme="dark"] .select2-container--default .select2-selection--multiple {
|
||||
background: var(--body-bg, #1e1e1e) !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
border-color: var(--border-color, #3a3a3a) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-selection__rendered {
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-search--inline .select2-search__field,
|
||||
html[data-theme="dark"] .select2-container--default .select2-search--dropdown .select2-search__field {
|
||||
background: transparent !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
border-color: rgba(255, 255, 255, 0.14) !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-dropdown {
|
||||
background: var(--body-bg, #1e1e1e) !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
border-color: var(--border-color, #3a3a3a) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-results__option {
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
|
||||
background: rgba(255, 255, 255, 0.10) !important;
|
||||
color: var(--body-fg, #ffffff) !important;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .select2-container--default .select2-results__option--selected {
|
||||
background: rgba(255, 255, 255, 0.06) !important;
|
||||
color: var(--body-fg, #e6e6e6) !important;
|
||||
}
|
||||
|
||||
@@ -18,5 +18,25 @@ _tmr.push({id: "3744288", type: "pageView", start: (new Date()).getTime()});
|
||||
f();
|
||||
}
|
||||
})(document, window, "tmr-code");
|
||||
// //Rating Mail.ru counter
|
||||
// Yandex.Metrika counter
|
||||
(function(m,e,t,r,i,k,a){
|
||||
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
|
||||
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=106953063', 'ym');
|
||||
ym(106953063, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
|
||||
// Google Analytics (GA4) counter
|
||||
(function() {
|
||||
var gaScript = document.createElement('script');
|
||||
gaScript.async = true;
|
||||
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-WTJM8J9YL5';
|
||||
document.head.appendChild(gaScript);
|
||||
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
// Делаем функцию глобально доступной, если понадобится вызывать её из других скриптов
|
||||
window.gtag = gtag;
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-WTJM8J9YL5');
|
||||
})();
|
||||
|
||||
@@ -12,11 +12,12 @@ django = "^6.0.2"
|
||||
django-taggit = "^6.1.0"
|
||||
pillow = "^12.1.1"
|
||||
pytils = "^0.4.4"
|
||||
etpgrf = "^0.1.4"
|
||||
django-environ = "^0.12.1"
|
||||
etpgrf = "^0.1.6"
|
||||
django-environ = "^0.13.0"
|
||||
whitenoise = "^6.11.0"
|
||||
gunicorn = "^25.1.0"
|
||||
tqdm = "^4.67.3"
|
||||
django-select2 = "^8.4.8"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
Reference in New Issue
Block a user