Compare commits
27 Commits
1a52626440
...
v0.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 6093dc2021 | |||
| 2a37e24242 | |||
| c529a74715 | |||
| 5f4769d10f | |||
| 261b72b04a | |||
| b128bbd79e | |||
| b97e82bcbb | |||
| 0b3eb517fe | |||
| 711b34af3d | |||
| 20d78194d2 | |||
| 37719ed31e | |||
| a9fb77c195 | |||
| 1b0fa5e500 | |||
| bbf35c0c24 | |||
| 08668fee6d | |||
| d846542e34 | |||
| 792d152be2 | |||
| 6e7a4c52e0 | |||
| 8385e04103 | |||
| 55980a0659 | |||
| ef80a66b69 | |||
| 493de32998 | |||
| 31cd78079a | |||
| 1a2865bae7 | |||
| d9e8c2d8bd | |||
|
|
a007337f75 | ||
|
|
c91d5f00b0 |
118
.dockerignore
Normal file
@@ -0,0 +1,118 @@
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
.github
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
.venv
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Тестирование и coverage
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
.dockerignore
|
||||
|
||||
# Документация и логи
|
||||
logs/
|
||||
*.log
|
||||
README.md
|
||||
target.md
|
||||
test_frontend.txt
|
||||
feedback.txt
|
||||
my_anwer.txt
|
||||
requare_dev_prod.txt
|
||||
requare_dev_w_home.txt
|
||||
|
||||
# Оптимизация статики - исключаем лишние файлы для компактного контейнера
|
||||
|
||||
# Source maps (нужны только для разработки, не для production)
|
||||
public/static/**/*.map
|
||||
staticfiles/**/*.map
|
||||
|
||||
# RTL версии Bootstrap (если не используется для арабских/персидских языков)
|
||||
public/static/css/bootstrap*.rtl.*
|
||||
public/static/css/v*.rtl.*
|
||||
staticfiles/css/bootstrap*.rtl.*
|
||||
staticfiles/css/v*.rtl.*
|
||||
|
||||
# TTF шрифты - используем только woff2 (меньше размер, лучше поддержка в браузерах)
|
||||
public/static/webfonts/*.ttf
|
||||
public/static/webfonts/*.eot
|
||||
public/static/webfonts/*.svg
|
||||
staticfiles/webfonts/*.ttf
|
||||
staticfiles/webfonts/*.eot
|
||||
staticfiles/webfonts/*.svg
|
||||
|
||||
# Обычные (неминифицированные) версии CSS для bootstrap - используем только min версии
|
||||
# (но оставляем rosmorport.css - наш проектный CSS)
|
||||
# bootstrap.css, bootstrap-grid.css, bootstrap-utilities.css, etc
|
||||
public/static/css/bootstrap.css
|
||||
public/static/css/bootstrap-grid.css
|
||||
public/static/css/bootstrap-utilities.css
|
||||
public/static/css/bootstrap-reboot.css
|
||||
public/static/css/v*.css
|
||||
public/static/css/svg-with-js.css
|
||||
public/static/css/fontawesome.css
|
||||
public/static/css/regular.css
|
||||
public/static/css/solid.css
|
||||
public/static/css/brands.css
|
||||
public/static/css/all.css
|
||||
staticfiles/css/bootstrap.css
|
||||
staticfiles/css/bootstrap-grid.css
|
||||
staticfiles/css/bootstrap-utilities.css
|
||||
staticfiles/css/bootstrap-reboot.css
|
||||
staticfiles/css/v*.css
|
||||
staticfiles/css/svg-with-js.css
|
||||
staticfiles/css/fontawesome.css
|
||||
staticfiles/css/regular.css
|
||||
staticfiles/css/solid.css
|
||||
staticfiles/css/brands.css
|
||||
staticfiles/css/all.css
|
||||
|
||||
# Прочее
|
||||
.editorconfig
|
||||
*.md
|
||||
|
||||
20
.env.example
Normal file
@@ -0,0 +1,20 @@
|
||||
# ========================================
|
||||
# Django настройки
|
||||
# ========================================
|
||||
DEBUG=True
|
||||
ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0
|
||||
SECRET_KEY=django-insecure-dev-secret-key-change-in-production-12345678
|
||||
|
||||
# Скрытый URL для админ панели (обфускированный для безопасности)
|
||||
# Рекомендуется использовать что-то сложное, например случайную строку
|
||||
# НИКОГДА не используй 'admin/' в production!
|
||||
ADMIN_URL=hidden-admin-panel/
|
||||
|
||||
# ========================================
|
||||
# База данных
|
||||
# ========================================
|
||||
DB_NAME=db.sqlite3
|
||||
|
||||
# Настройки достпа к пакетам в репозитории, чтобы wathtower мог проверять их свежесть и скачивать
|
||||
REPO_USER=xxxxx
|
||||
REPO_PASS=xxxxx
|
||||
58
.gitea/workflows/docker-publish.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Build and Push PetClones-site
|
||||
|
||||
on:
|
||||
push:
|
||||
# Запускать только при создании тега, начинающегося с 'v' (например, v1.0.0)
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
# Генерируем метаданные (теги) для Docker
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
# Важно: используем gitea.repository
|
||||
images: git.cube2.ru/${{ gitea.repository }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=raw,value=latest,enable=${{ gitea.ref_type == 'tag' }}
|
||||
|
||||
# Логин в реестр Gitea
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.cube2.ru
|
||||
# Важно: используем gitea.actor
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
# Сборка и отправка образа
|
||||
- name: Build and push image PetClones-site
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
# Используем только amd64 для надежности (arm64 добавим позже если нужно)
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
# Используем теги, сгенерированные шагом meta (v0.1.0 и latest)
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# Кэширование для ускорения повторных сборок
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
# Увеличиваем тайм-аут для надежности (от 1800 до 3600 сек)
|
||||
timeout: 3600 # 60 минут (для надежности при медленном интернете)
|
||||
9
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Вы - опытный помощник по программированию GitHub Copilot.
|
||||
Всегда отвечай на русском языке, если не указано иное.
|
||||
Комментируй код, который ты генерируешь, чтобы объяснить его функциональность на русском языке.
|
||||
Не удаляй старые комментарии. Код которых хочешь удалить скрывай в комментариях.
|
||||
При написании кода следуй стандартам PEP 8 для Python.
|
||||
Используй современные возможности Python (3.12+) и Django (6.0+).
|
||||
Предпочитай `poetry` для управления зависимостями.
|
||||
Подробнее комментировать каждое действие.
|
||||
Всегда спрашивай да/нет пред деплоем, git commit и git push.
|
||||
30
.gitignore
vendored
@@ -24,6 +24,9 @@
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# IDE - GitHub Copilot state (local IDE state, not needed in repo)
|
||||
.idea/copilotDiffState.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
@@ -89,7 +92,30 @@ media
|
||||
|
||||
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
|
||||
# in your Git repository. Update and uncomment the following line accordingly.
|
||||
# <django-project-name>/staticfiles/
|
||||
staticfiles/
|
||||
|
||||
# SPECIFIC FOR THE PROJECT
|
||||
my_secret*.*
|
||||
my_secret*.*
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Poetry
|
||||
poetry.lock
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Static files in development
|
||||
public/static/
|
||||
public/media/
|
||||
|
||||
# Python virtual environments
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
|
||||
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
29
.idea/2024-test-rosmorport.iml
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$/rosmorport_tsts" />
|
||||
<option name="settingsModule" value="rosmorport_tsts/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/rosmorport_tsts/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/rosmorport_tsts" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.14 (rosmorport-tsts-OiaYl72h-py3.14)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Ask2AgentMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<Languages>
|
||||
<language minSize="57" name="Python" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.8" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14 (rosmorport-tsts-OiaYl72h-py3.14)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/2024-test-rosmorport.iml" filepath="$PROJECT_DIR$/.idea/2024-test-rosmorport.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
99
Dockerfile
Normal file
@@ -0,0 +1,99 @@
|
||||
# Dockerfile для Django приложения rosmorport_tsts
|
||||
# Многоэтапная сборка для оптимизации размера образа
|
||||
|
||||
# Первый этап: builder - установка зависимостей
|
||||
FROM python:3.12-slim AS builder
|
||||
|
||||
# Переводим в режим без буферизации для вывода логов
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Устанавливаем poetry
|
||||
RUN pip install --no-cache-dir poetry==1.8.3
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем файлы с информацией о зависимостях
|
||||
COPY pyproject.toml poetry.lock* ./
|
||||
|
||||
# Экспортируем зависимости из poetry в requirements.txt
|
||||
# Это более надежный способ в Docker, чем использование виртуального окружения poetry
|
||||
RUN poetry export -f requirements.txt --output requirements.txt --no-interaction
|
||||
|
||||
# Создаем виртуальное окружение в стандартном месте
|
||||
RUN python -m venv /opt/venv
|
||||
|
||||
# Устанавливаем зависимости в виртуальное окружение
|
||||
RUN /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Второй этап: runtime - финальный образ
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Переводим в режим без буферизации для вывода логов
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
# Установка PATH для виртуального окружения перед копированием
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Устанавливаем пользователя для запуска приложения (из соображений безопасности)
|
||||
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# Копируем виртуальное окружение из builder
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
|
||||
# Копируем содержимое проекта
|
||||
COPY --chown=appuser:appuser . .
|
||||
|
||||
# Создаём директорию для собранной статики и даём права пользователю app
|
||||
RUN mkdir -p /app/staticfiles && chown -R appuser:appuser /app/staticfiles
|
||||
|
||||
# Создаём директорию для media
|
||||
RUN mkdir -p /app/public/media && chown -R appuser:appuser /app/public/media
|
||||
|
||||
# Создаём директорию для БД и даём права пользователю app
|
||||
# Это важно когда БД монтируется как том с хоста
|
||||
RUN mkdir -p /app/database && chown -R appuser:appuser /app/database
|
||||
|
||||
# Копируем внешний nginx конфиг для экспорта на хост (через volume)
|
||||
# Это нужно для настройки reverse-proxy на хосте
|
||||
COPY config/nginx/pet-clones--external-nginx.conf /tmp/pet-clones--external-nginx.conf.source
|
||||
|
||||
# Собираем статику при сборке образа (для production)
|
||||
# Это гарантирует что статика готова и не нужно запускать collectstatic при старте
|
||||
# PATH уже установлен в переменных окружения выше, поэтому Django должен быть найден
|
||||
RUN cd /app/rosmorport_tsts && \
|
||||
python manage.py collectstatic --noinput --clear --no-input
|
||||
|
||||
# Оптимизация размера контейнера: удаляем лишние файлы статики
|
||||
# Source maps нужны только для разработки
|
||||
RUN find /app/staticfiles -name "*.map" -delete
|
||||
|
||||
# Удаляем RTL версии Bootstrap (если проект не поддерживает RTL)
|
||||
RUN find /app/staticfiles -name "*rtl*" -type f -delete
|
||||
|
||||
# Удаляем TTF и EOT шрифты (используем только woff2 - меньше размер)
|
||||
RUN find /app/staticfiles/webfonts -type f \( -name "*.ttf" -o -name "*.eot" -o -name "*.svg" \) -delete
|
||||
|
||||
# Удаляем неминифицированные версии CSS (кроме нашего собственного rosmorport.css)
|
||||
# bootstrap.css, all.css, fontawesome.css используем только .min версии
|
||||
RUN find /app/staticfiles/css -maxdepth 1 -name "*.css" -not -name "*.min.css" -not -name "rosmorport.css" -delete
|
||||
|
||||
# Открываем порт
|
||||
EXPOSE 8000
|
||||
|
||||
# CMD - выполняет миграции и запускает Gunicorn
|
||||
# Миграции выполняются при каждом запуске для гибкости
|
||||
# Статика уже собрана при сборке образа
|
||||
# Параметры оптимизированы для минимальной нагрузкой (ветер качает, но не падает):
|
||||
# - bind 0.0.0.0:8000 (все интерфейсы контейнера, необходимо для Docker проброса портов)
|
||||
# - workers=1 (один worker - достаточно если не будет нагрузок)
|
||||
# - timeout=120 (сокращенный таймаут - запросы должны быть быстрыми)
|
||||
# - max-requests=200 (перезагрузка воркера после 200 запросов - экономия памяти)
|
||||
# - access-logfile/error-logfile '-' (логи в stdout для docker)
|
||||
CMD ["sh", "-c", "cd /app/rosmorport_tsts && echo '>>> Applying database migrations...' && python manage.py migrate --noinput && echo '>>> Starting Gunicorn...' && gunicorn --bind 0.0.0.0:8000 --workers 1 --worker-class sync --worker-tmp-dir /dev/shm --max-requests 200 --timeout 120 --access-logfile - --error-logfile - rosmorport_tsts.wsgi:application"]
|
||||
|
||||
10
README.md
@@ -1,14 +1,20 @@
|
||||
## Тестовое задание rosmorport
|
||||
|
||||
**2024 год**: _14 апреля (воскресенье)_
|
||||
|
||||
Тестовое задание на соискание вакансии `Frontend-разработчик` в ФГУП РосМорФлот.
|
||||
|
||||
* [Тестовое задание](target.md) (таким, как оно было выдано, авторская орфография и пунктуация).
|
||||
* [Мой ответ](my_anwer.txt) (спустя два дня)
|
||||
* [Ответ РосМорФлот](feedback.txt) (спустя четыре дня, в ответ на просьбу обратной связи... судя по логам
|
||||
заходил один раз сразу по получения решения).
|
||||
* Развёрнуто на [pet-clones.cube2.ru](https://pet-clones.cube2.ru/) (login: admin / pwd: 1234)
|
||||
* [Ответ РосМорФлот](feedback.txt) спустя четыре дня, после нескольких обращений об обратной связи... судя по логам
|
||||
заходил один раз, никих действий (новых записей) произведено не было.
|
||||
|
||||
Мое мнение о тестовом задании: в задании нет никаких рекомендаций, ограничений и запретов на
|
||||
какие-либо _выбранные технологии_... Зачем задании столько требований по Backend, если вакансия
|
||||
`Frontend-разработчик` -- загадка. И, похоже, дополнительно нужен навык чтения мыслей на расстоянии.
|
||||
|
||||
---
|
||||
|
||||
**2026 год**: _1 апреля (среда)_
|
||||
Проект, при переносе на новый хостинг, переделан для развертывания в Docker. Просто жалко выбрасывать.
|
||||
122
config/nginx/pet-clones--external-nginx.conf
Normal file
@@ -0,0 +1,122 @@
|
||||
# == Конфикурационный файл config/nginx/pet-clones--external-nginx.conf ==
|
||||
|
||||
# Внешний nginx конфиг для проксирования к контейнеру petclones-site--backend
|
||||
# Это конфиг для ХОСТА (не внутри контейнера)
|
||||
|
||||
# Upstream (группировка backends) для проксирования запросов в Django контейнер
|
||||
upstream petclones-backend {
|
||||
# Контейнер слушает на 127.0.0.1:8040 согласно docker-compose.prod.yml
|
||||
server 127.0.0.1:8040;
|
||||
# Сохраняем persistent соединения (требует proxy_http_version 1.1 и proxy_set_header Connection "")
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Основной server блок для обработки HTTP запросов
|
||||
server {
|
||||
# Слушаем на стандартном HTTP порту (80) для IPv4 и IPv6
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name pet-clones.cube2.ru;
|
||||
|
||||
# Редирект HTTP → HTTPS (будет активирован после добавления SSL сертификата через certbot)
|
||||
# Раскомментируй эту строку после получения SSL сертификата:
|
||||
# return 301 https://$server_name$request_uri;
|
||||
|
||||
# Логирование запросов и ошибок для этого виртуального хоста
|
||||
access_log /var/log/nginx/petclones.access.log;
|
||||
error_log /var/log/nginx/petclones.error.log warn;
|
||||
|
||||
# ОСНОВНОЙ БЛОК ПРОКСИРОВАНИЯ
|
||||
# Все запросы, кроме перехвачённых специфичными location'ами, проксируются сюда
|
||||
location / {
|
||||
# Проксируем все запросы в upstream контейнер
|
||||
proxy_pass http://petclones-backend;
|
||||
|
||||
# --- ЗАГОЛОВКИ ДЛЯ КОРРЕКТНОЙ РАБОТЫ ПРИЛОЖЕНИЯ ---
|
||||
# Передаём оригинальный хост (важно для правильной работы Django и генерации URLs)
|
||||
proxy_set_header Host $host;
|
||||
# Передаём оригинальный IP клиента (для логирования и rate-limiting)
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
# Добавляем этот прокси в цепь X-Forwarded-For (если было несколько проксей)
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# Передаём оригинальный протокол (HTTP или HTTPS) - важно для правильной работы redirect'ов
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# Передаём оригинальный хост (может отличаться от Host если используется load balancer)
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
# Передаём порт если не стандартный
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# --- HTTP KEEP-ALIVE ДЛЯ ПРОИЗВОДИТЕЛЬНОСТИ ---
|
||||
# Используем HTTP 1.1 для поддержки keep-alive
|
||||
proxy_http_version 1.1;
|
||||
# Очищаем Connection header (иначе будет "Connection: close")
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# --- ТАЙМАУТЫ ДЛЯ ДЛИТЕЛЬНЫХ ЗАПРОСОВ ---
|
||||
# Время ожидания подключения к backend
|
||||
proxy_connect_timeout 60s;
|
||||
# Время ожидания отправки запроса на backend
|
||||
proxy_send_timeout 60s;
|
||||
# Время ожидания ответа от backend (может быть увеличено если есть долгие запросы)
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# --- БУФЕРИЗАЦИЯ ОТВЕТОВ ---
|
||||
# Включаем буферизацию (nginx буферизирует ответ перед отправкой клиенту)
|
||||
# Это экономит память backend если клиент медленный
|
||||
proxy_buffering on;
|
||||
# Размер буфера для первой части ответа (заголовки)
|
||||
proxy_buffer_size 4k;
|
||||
# Количество и размер буферов для основного тела ответа
|
||||
proxy_buffers 8 4k;
|
||||
}
|
||||
|
||||
# КЭШИРОВАНИЕ СТАТИЧЕСКИХ ФАЙЛОВ
|
||||
# Кэшируем: HTML, изображения, стили, скрипты, шрифты и иконки
|
||||
# ВАЖНО: WhiteNoise уже сжимает статику в контейнере (Gzip в nginx не нужен)
|
||||
location ~* \.(htm|html|gif|png|jpe?g|svg|ico|css|js|woff|woff2|ttf|eot)$ {
|
||||
# Проксируем запрос к backend (статика часть контейнера)
|
||||
proxy_pass http://petclones-backend;
|
||||
# Передаём хост в заголовке
|
||||
proxy_set_header Host $host;
|
||||
|
||||
# --- КЭШИРОВАНИЕ В БРАУЗЕРЕ КЛИЕНТА ---
|
||||
# Если файлы имеют хеш в названии (например: app.abc123.js), то cache-control может быть очень долгой
|
||||
# "immutable" говорит браузеру что файл никогда не изменится (если изменится, то будет другой хеш)
|
||||
add_header Cache-Control "public, max-age=2592000"; # 30 дней в секундах (30*24*60*60)
|
||||
}
|
||||
|
||||
# КЭШИРОВАНИЕ МЕДИА ФАЙЛОВ
|
||||
# Медиа загруженные пользователями кэшируются меньше чем статика (на случай удаления или обновления)
|
||||
location ~* ^/media/.*\.(jpg|jpeg|png|gif|webp|svg|pdf|mp4|webm)$ {
|
||||
proxy_pass http://petclones-backend;
|
||||
proxy_set_header Host $host;
|
||||
# Кэшируем на 30 дней (можно менять в зависимости от политики)
|
||||
add_header Cache-Control "public, max-age=604800, immutable"; # 7 дней в секундах (604800 = 7*24*60*60)
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS SERVER БЛОК (ДОБАВИТЬ ПОСЛЕ ПОЛУЧЕНИЯ SSL СЕРТИФИКАТА)
|
||||
# После получения SSL сертификата через certbot, раскомментируй и настрой эту секцию:
|
||||
#
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# listen [::]:443 ssl http2;
|
||||
# server_name pet-clones.cube2.ru;
|
||||
#
|
||||
# # SSL сертификаты от certbot
|
||||
# ssl_certificate /etc/letsencrypt/live/pet-clones.cube2.ru/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/pet-clones.cube2.ru/privkey.pem;
|
||||
#
|
||||
# # Параметры безопасности SSL
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
# ssl_prefer_server_ciphers on;
|
||||
#
|
||||
# # остальная конфигурация идентична HTTP блоку выше...
|
||||
# # (копируешь content из server блока выше и вставляешь сюда)
|
||||
# }
|
||||
#
|
||||
# ТАКЖЕ НЕ ЗАБУДЬ:
|
||||
# 1. Установи certbot: sudo apt-get install certbot python3-certbot-nginx
|
||||
# 2. Получи сертификат: sudo certbot certonly --standalone -d pet-clones.cube2.ru
|
||||
# 3. Перезагрузи nginx: sudo systemctl restart nginx
|
||||
@@ -1,70 +0,0 @@
|
||||
# ТЕСТОВОЕ ЗАДАНИЕ РОСМОРПОРТ
|
||||
# == Конфикурационный файл pet-clones--nginx.conf ==
|
||||
|
||||
# Описываем апстрим-потоки которые должен подключить Nginx
|
||||
# Для каждого сайта надо настроить свой поток, со своим уникальным именем.
|
||||
# Если будете настраивать несколько python (django) сайтов - измените название upstream
|
||||
|
||||
upstream pet-clone {
|
||||
# расположение файла Unix-сокет для взаимодействие с uwsgi
|
||||
server unix:///home/web/pet-clones.cocorico.ru/socket/clone_pets.sock;
|
||||
}
|
||||
|
||||
# конфигурируем сервер
|
||||
server {
|
||||
server_name pet-clones.cocorico.ru; # доменное имя сайта
|
||||
listen 443 ssl; # managed by Certbot
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/pet-clones.cocorico.ru/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/pet-clones.cocorico.ru/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
# ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
|
||||
ssl_session_cache builtin:1000 shared:SSL:25m;
|
||||
keepalive_timeout 75s;
|
||||
|
||||
charset utf-8; # кодировка по умолчанию
|
||||
|
||||
access_log /home/web/pet-clones.cocorico.ru/logs/clone-pets-access.log; # логи с доступом
|
||||
error_log /home/web/pet-clones.cocorico.ru/logs/clone-pets-error.log; # логи с ошибками
|
||||
|
||||
client_max_body_size 100M; # максимальный объем файла для загрузки на сайт (max upload size)
|
||||
|
||||
error_page 404 /404.html;
|
||||
error_page 500 /500.html;
|
||||
|
||||
location /media { alias /home/web/pet-clones.cocorico.ru/public/media; } # Расположение media-файлов Django
|
||||
location /static { alias /home/web/pet-clones.cocorico.ru/public/static; } # Расположение static-файлов Django
|
||||
|
||||
location /robots.txt { root /home/web/pet-clones.cocorico.ru/public; } # Расположение robots.txt
|
||||
location /favicon.ico { root /home/web/pet-clones.cocorico.ru/public/static/img; } # Расположение favicon.ico
|
||||
location /favicon.png { root /home/web/pet-clones.cocorico.ru/public/static/img; } # Расположение favicon
|
||||
location = /404.html {
|
||||
root /home/web/pet-clones.cocorico.ru/rosmorport_tsts/templates-django/404.html;
|
||||
internal;
|
||||
}
|
||||
location = /500.html {
|
||||
root /home/web/pet-clones.cocorico.ru/rosmorport_tsts/templates-django/500.html;
|
||||
internal;
|
||||
}
|
||||
location ~ \.(xml|html|htm|txt|svg)$ {
|
||||
root /home/web/pet-clones.cocorico.ru/public; # Расположение статичных *.xml, *.html и *.txt
|
||||
}
|
||||
|
||||
location / {
|
||||
uwsgi_pass pet-clone; # upstream обрабатывающий обращений
|
||||
include uwsgi_params; # конфигурационный файл uwsgi;
|
||||
uwsgi_read_timeout 1800; # вдруг некоторые запросы очень долго обрабатываются?
|
||||
uwsgi_send_timeout 200; # на всякий случай время записи в сокет тоже побольше...
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host = pet-clones.cocorico.ru) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
server_name pet-clones.cocorico.ru;
|
||||
listen 80;
|
||||
return 404; # managed by Certbot
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
# === Конфигурационный файл uwsgi pets-clone--uwsgi.ini ===
|
||||
# ░▒▓████▓▒░▒▓█████▓▒░▒▓█▓▒░ ▒▓██████▓▒░ ВНИМАНИЕ ДЛЯ ТЕХ КТО ДЕПЛОИТ ИЗ ПОД MICROSOFT WINDOWS!!
|
||||
# ░▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒░ ▒▓█▓▒░ ОКОНЧАНИЯ СТРОК ДОЛЖНЫ БЫТЬ В ФОРМАТЕ UNIX (LF) а не CR+LR (как в Win)
|
||||
#-░▒▓█▓▒░----▒▓█████▓▒-▒▓█▓▒░---▒▓████▓▒░----------------------------------------------------------------
|
||||
# ░▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒░ ▒▓█▓▒░ Заметка: CR - carriage return (\r, возврат каретки, ВК)
|
||||
# ░▒▓████▓▒░▒▓█▓▒▒▓█▓▒▒▓█████▓▒▒▓█▓▒░ LF - line feed (\n, перевод строки, ПС)
|
||||
|
||||
[uwsgi]
|
||||
|
||||
# НАСТРОЙКИ ДЛЯ DJANGO
|
||||
# Корневая папка проекта (полный путь)
|
||||
chdir = /home/web/pet-clones.cocorico.ru/rosmorport_tsts
|
||||
module = rosmorport_tsts.wsgi
|
||||
# полный путь к виртуальному окружению
|
||||
home = /home/web/pet-clones.cocorico.ru/env
|
||||
# полный путь к файлу сокета
|
||||
socket = /home/web/pet-clones.cocorico.ru/socket/clone_pets.sock
|
||||
|
||||
# ЗАГАДОЧНЫЕ НАСТРОЙКИ, ПО ИДЕЕ ОНИ НУЖНЫ, НО И БЕЗ НИХ ВСЁ РАБОТАЕТ
|
||||
# расположение wsgi.py
|
||||
wsgi-file = /home/web/pet-clones.cocorico.ru/rosmorport_tsts/rosmorport_tsts/wsgi.py
|
||||
# расположение виртуального окружения (как оно работает если этот параметр не указан, не ясно)
|
||||
virtualenv = /home/web/pet-clones.cocorico.ru/env
|
||||
# имя файла при изменении которого происходит авторестарт приложения
|
||||
# (когда этого параметра нет, то гичего не авторестартится, но с ним все рестартится.
|
||||
# Cтоит изменить любой Python-исходник проекта, как изменения сразу вступают в силу.
|
||||
touch-reload = /home/web/pet-clones.cocorico.ru/logs/touchreload.txt
|
||||
py-autoreload = 5
|
||||
|
||||
#
|
||||
# НАСТРОЙКИ ОБЩИЕ
|
||||
# быть master-процессом
|
||||
master = true
|
||||
# максимальное количество процессов
|
||||
processes = 2
|
||||
# если uWSGI устнаовлен как сервис через apt-get то нужно установить еще плугин:
|
||||
# sudo apt-get install uwsgi-plugin-python
|
||||
# и добавить в этот конфиг: plugin = python
|
||||
plugin = python3
|
||||
# права доступа к файлу сокета. По умолчанию должно хватать 664. Но каких-то прав не хватает, поэтому 666.
|
||||
chmod-socket = 666
|
||||
# очищать окружение от служебных файлов uwsgi по завершению
|
||||
vacuum = true
|
||||
# количество секунд после которых подвисший процес будет перезапущен
|
||||
# Так как некоторе скрипты требуют изрядно времени (особенно полная переиндексация) то ставим значение побольще
|
||||
harakiri = 2600
|
||||
# В общем случае, при некотых значениях harakiri логах uWSGI может вываливаться предупреждение:
|
||||
# WARNING: you have enabled harakiri without post buffering. Slow upload could be rejected on post-unbuffered webservers
|
||||
# можно оставить harakiri закоментированным, но нам нужно 900 и на него не ругается. Ругается на 30.
|
||||
|
||||
# разрешаем многопоточность
|
||||
enable-threads = true
|
||||
vacuum = true
|
||||
thunder-lock = true
|
||||
max-requests = 500
|
||||
|
||||
# пользователь и группа пользователей от имени которых запускать uWSGI
|
||||
# указываем www-data: к этой группе относится nginz, и ранее мы включили в эту группу нашего [user]
|
||||
# uid = nginx
|
||||
# gid = nginx
|
||||
# uid = root
|
||||
# gid = root
|
||||
uid = web
|
||||
gid = web
|
||||
|
||||
print = ---------------- Запущен uWSGI для pet-clones.cocorico.ru ---------------
|
||||
0
database/.gitkeep
Normal file
107
docker-compose.prod.yml
Normal file
@@ -0,0 +1,107 @@
|
||||
% cat docker-compose.yml
|
||||
# docker-compose.prod.yml для production развертывания проекта rosmorport_tsts
|
||||
# Для использование на хосте провайдера, перейдите в папку с приложением, размемтите
|
||||
# его там под именем `docker-compose.yml`
|
||||
#
|
||||
# Запуск на продакшене:
|
||||
# cd <папка-с-приложением>
|
||||
# mkdir -p config/nginx media database
|
||||
# sudo chmod -R 777 media database config/nginx
|
||||
# sodo docker compose up -d
|
||||
|
||||
# version: '3.9'
|
||||
|
||||
services:
|
||||
# Django приложение в production
|
||||
web:
|
||||
# Используем образ из Gitea реестра (собран в CI/CD)
|
||||
image: git.cube2.ru/erjemin/2024-test-rosmorport:latest
|
||||
|
||||
# Имя контейнера
|
||||
container_name: petclones-site--backend
|
||||
|
||||
# Переменные окружения
|
||||
env_file:
|
||||
- .env
|
||||
# Переменные окружения для production
|
||||
environment:
|
||||
- DOCKER_ENV=1
|
||||
- DEBUG=False
|
||||
- PYTHONUNBUFFERED=1
|
||||
|
||||
# Тома для медиа, статики и БД
|
||||
# ВАЖНО: Используем локальные монтирования, а не именованные тома!
|
||||
# Данные должны быть доступны на хосте для nginx и резервного копирования
|
||||
volumes:
|
||||
# Локальная папка для media (загруженные пользователями файлы)
|
||||
# Контейнер: /app/public/media → Хост: ./media
|
||||
- ./media:/app/public/media:rw
|
||||
# Локальная папка для базы данных (SQLite файл)
|
||||
# Контейнер: /app/database → Хост: ./database
|
||||
- ./database:/app/database:rw
|
||||
# Volume для экспорта конфигов из контейнера на хост
|
||||
# Контейнер: /tmp/nginx_configs → Хост: ./config/nginx
|
||||
- ./config/nginx:/tmp/nginx_configs:rw
|
||||
|
||||
# Command: запуск Django приложения с миграциями
|
||||
command: >
|
||||
sh -c "
|
||||
cd /app/rosmorport_tsts &&
|
||||
echo 'Применение миграций БД...' &&
|
||||
python manage.py migrate --noinput &&
|
||||
echo 'Сбор статических файлов...' &&
|
||||
python manage.py collectstatic --noinput &&
|
||||
echo '🚀 Запуск gunicorn...' &&
|
||||
gunicorn --bind 0.0.0.0:8000 --workers 1 --worker-class sync --worker-tmp-dir /dev/shm --max-requests 200 --timeout 120 --access-logfile - --error-logfile - rosmorport_tsts.wsgi:application
|
||||
"
|
||||
|
||||
# Перенаправляем порты
|
||||
ports:
|
||||
- "127.0.0.1:8040:8000"
|
||||
|
||||
# Политика перезапуска
|
||||
restart: unless-stopped
|
||||
|
||||
# Метки для Watchtower (авто-обновление)
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.scope=petclones-site--scope"
|
||||
|
||||
# Ограничения ресурсов
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.25'
|
||||
memory: 256M
|
||||
|
||||
# Логирование в JSON-файлы (для сбора логов через docker logs)
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "5m" # больше лимит в продакшене для логирования
|
||||
max-file: "1" # храним 1 файл лога
|
||||
|
||||
# WATCHTOWER ДЛЯ АВТОМАТИЧЕСКОГО ОБНОВЛЕНИЯ ОБРАЗОВ ИЗ РЕЕСТРА
|
||||
watchtower:
|
||||
image: containrrr/watchtower
|
||||
container_name: petclones-site--watchtower
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# Переменные окружения
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- DOCKER_API_VERSION=1.44
|
||||
# Берем учетные данные из .env файла
|
||||
- REPO_USER=${REPO_USER}
|
||||
- REPO_PASS=${REPO_PASS}
|
||||
# Ограничиваем область видимости только этим проектом
|
||||
- WATCHTOWER_SCOPE=petclones-site--scope
|
||||
# Если нужно указать реестр явно (обычно watchtower сам понимает из имени образа)
|
||||
# - WATCHTOWER_REGISTRY_URL=git.cube2.ru
|
||||
command: --interval 1800 --cleanup # Проверять каждые 30 минут
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
41
docker-compose.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
# docker-compose.yml для локальной разработки проекта rosmorport_tsts
|
||||
# Использование: docker-compose up
|
||||
|
||||
# version: '3.9'
|
||||
|
||||
services:
|
||||
# Django приложение
|
||||
web:
|
||||
# Строим образ из Dockerfile в текущей директории
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
||||
# Имя контейнера
|
||||
container_name: rosmorpor-tst-site--backend
|
||||
|
||||
# Переменные окружения для разработки
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- DOCKER_ENV=1
|
||||
- DJANGO_LOG_LEVEL=DEBUG
|
||||
- DEBUG=True
|
||||
- PYTHONUNBUFFERED=1
|
||||
|
||||
# Монтируем текущую директорию для разработки
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./public/media:/app/public/media
|
||||
- ./database:/app/database
|
||||
|
||||
# Перенаправляем порты
|
||||
ports:
|
||||
- "127.0.0.1:8040:8000"
|
||||
|
||||
# Сетевая конфигурация
|
||||
networks:
|
||||
default:
|
||||
name: rosmorport_network
|
||||
driver: bridge
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.
|
||||
135
poetry.lock
generated
Normal file
@@ -0,0 +1,135 @@
|
||||
# This file is automatically @generated by Poetry 1.8.0 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.11.1"
|
||||
description = "ASGI specs, helper code, and adapters"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133"},
|
||||
{file = "asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.12"
|
||||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "django-5.2.12-py3-none-any.whl", hash = "sha256:4853482f395c3a151937f6991272540fcbf531464f254a347bf7c89f53c8cff7"},
|
||||
{file = "django-5.2.12.tar.gz", hash = "sha256:6b809af7165c73eff5ce1c87fdae75d4da6520d6667f86401ecf55b681eb1eeb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asgiref = ">=3.8.1"
|
||||
sqlparse = ">=0.3.1"
|
||||
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
argon2 = ["argon2-cffi (>=19.1.0)"]
|
||||
bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "21.2.0"
|
||||
description = "WSGI HTTP Server for UNIX"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"},
|
||||
{file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
packaging = "*"
|
||||
|
||||
[package.extras]
|
||||
eventlet = ["eventlet (>=0.24.1)"]
|
||||
gevent = ["gevent (>=1.4.0)"]
|
||||
setproctitle = ["setproctitle"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"},
|
||||
{file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.2.2"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"},
|
||||
{file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytils-safe"
|
||||
version = "0.3.2"
|
||||
description = "Russian-specific string utils"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pytils_safe-0.3.2.tar.gz", hash = "sha256:67edae8897a8e03f422c3e1d01a5dd4c9d11ce3162e97c731640db052442714b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.5"
|
||||
description = "A non-validating SQL parser."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba"},
|
||||
{file = "sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["build"]
|
||||
doc = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.3"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
{file = "tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1"},
|
||||
{file = "tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whitenoise"
|
||||
version = "6.12.0"
|
||||
description = "Radically simplified static file serving for WSGI applications"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2"},
|
||||
{file = "whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "2c2c5c9d6e4ef7bbc358e1a2142f4a9943cbf4b641c770fcd5748ca07c02abe4"
|
||||
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
public/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
3
public/favicon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"><metadata></metadata><circle cx="32" cy="32" r="32" fill="#ff576b"></circle><path d="M44.76 32c0-7.72-5.4-11.79-11-15.52 4.63-3.07 9.12-6.41 10.53-11.83 0-.01 0-.02.01-.02a14.61 14.61 0 0 0 .46-3.66c0-.54-.43-.97-.97-.97a.97.97 0 0 0-.97.97 13.37 13.37 0 0 1-.21 2.33h-7.3c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h6.76c-.42 1.13-1.01 2.17-1.72 3.12h-3.63a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h1.92c-1.88 1.84-4.22 3.43-6.64 5.02-2.42-1.59-4.76-3.18-6.64-5.02h2.24a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-3.95c-.71-.95-1.3-1.99-1.72-3.12h6.88a.98.98 0 0 0 .97-.97.97.97 0 0 0-.97-.97h-7.42a13.37 13.37 0 0 1-.21-2.33.97.97 0 0 0-.97-.97c-.54 0-.97.43-.97.97a14.61 14.61 0 0 0 .46 3.66c.01 0 .01.01.01.02 1.41 5.42 5.9 8.76 10.53 11.83-5.6 3.73-11 7.8-11 15.52s5.4 11.79 11 15.52c-2.83 1.88-5.61 3.86-7.67 6.35-1.31 1.59-2.32 3.38-2.86 5.49-.01.01-.01.02-.01.03a14.39 14.39 0 0 0-.46 3.64c0 .54.43.97.97.97a.97.97 0 0 0 .97-.97c0-.82.08-1.58.21-2.32h7.42a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-6.88c.43-1.18 1.05-2.25 1.8-3.24h3.93a.98.98 0 0 0 .97-.97.97.97 0 0 0-.97-.97h-2.19c1.86-1.8 4.15-3.35 6.53-4.91 2.38 1.56 4.67 3.11 6.53 4.91h-2.82c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h4.56c.75.99 1.37 2.06 1.8 3.24h-7.01a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h7.55c.13.74.21 1.5.21 2.32a.97.97 0 0 0 .97.97c.54 0 .97-.43.97-.97a14.39 14.39 0 0 0-.46-3.64c0-.01 0-.02-.01-.03-.54-2.11-1.55-3.9-2.86-5.49-2.06-2.49-4.84-4.47-7.67-6.35 5.6-3.73 11-7.8 11-15.52zm-9.7 9.95h2.92c-1.76 1.58-3.83 2.99-5.98 4.4-2.15-1.41-4.22-2.82-5.98-4.4h3.07a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-4.95c-1.12-1.34-1.98-2.82-2.48-4.54h6.99a.97.97 0 0 0 .97-.97c0-.54-.43-.97-.97-.97h-7.38c-.05-.49-.09-1-.09-1.53a12.57 12.57 0 0 1 .33-2.88h6.46a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-5.84c.51-1.16 1.19-2.22 2.01-3.19h6.25a.97.97 0 0 0 .97-.97c0-.54-.43-.97-.97-.97h-4.37c1.76-1.58 3.83-2.99 5.98-4.4 2.15 1.41 4.22 2.82 5.98 4.4h-4.85c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h6.73c.82.97 1.5 2.03 2.01 3.19h-6.81a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h7.43a12.57 12.57 0 0 1 .33 2.88c0 .53-.04 1.04-.09 1.53h-8.48c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h8.09c-.5 1.72-1.36 3.2-2.48 4.54h-4.8a.97.97 0 0 0-.97.97c0 .54.43.97.97.97z"></path><path fill="#6bc7db" d="M33.28 34.5a.97.97 0 0 0 .97.97h8.09c-.5 1.72-1.36 3.2-2.48 4.54h-4.8a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h2.92c-1.76 1.58-3.83 2.99-5.98 4.4-2.15-1.41-4.22-2.82-5.98-4.4h3.07a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-4.95c-1.12-1.34-1.98-2.82-2.48-4.54h6.99a.97.97 0 0 0 .97-.97c0-.54-.43-.97-.97-.97h-7.38L21.18 32a12.57 12.57 0 0 1 .33-2.88h6.46a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-5.84c.51-1.16 1.19-2.22 2.01-3.19h6.25a.97.97 0 0 0 .97-.97c0-.54-.43-.97-.97-.97h-4.37c1.76-1.58 3.83-2.99 5.98-4.4 2.15 1.41 4.22 2.82 5.98 4.4h-4.85c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h6.73c.82.97 1.5 2.03 2.01 3.19h-6.81a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h7.43a12.57 12.57 0 0 1 .33 2.88c0 .53-.04 1.04-.09 1.53h-8.48c-.54 0-.97.43-.97.97zM43.79 0a.97.97 0 0 0-.97.97 13.37 13.37 0 0 1-.21 2.33h-7.3c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h6.76c-.42 1.13-1.01 2.17-1.72 3.12h-3.63a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h1.92c-1.88 1.84-4.22 3.43-6.64 5.02-2.42-1.59-4.76-3.18-6.64-5.02h2.24a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-3.95c-.71-.95-1.3-1.99-1.72-3.12h6.88a.98.98 0 0 0 .97-.97.97.97 0 0 0-.97-.97h-7.42a13.37 13.37 0 0 1-.21-2.33.97.97 0 0 0-.97-.97h23.58zm0 64H20.21a.97.97 0 0 0 .97-.97c0-.82.08-1.58.21-2.32h7.42a.97.97 0 0 0 .97-.97.98.98 0 0 0-.97-.97h-6.88c.43-1.18 1.05-2.25 1.8-3.24h3.93a.98.98 0 0 0 .97-.97.97.97 0 0 0-.97-.97h-2.19c1.86-1.8 4.15-3.35 6.53-4.91 2.38 1.56 4.67 3.11 6.53 4.91h-2.82c-.54 0-.97.43-.97.97a.97.97 0 0 0 .97.97h4.56c.75.99 1.37 2.06 1.8 3.24h-7.01a.97.97 0 0 0-.97.97c0 .54.43.97.97.97h7.55c.13.74.21 1.5.21 2.32a.97.97 0 0 0 .97.97z"></path><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
|
||||
@media (prefers-color-scheme: dark) { :root { filter: none; } }
|
||||
</style></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
115
public/llms.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Тестовый проект "Клонирование животных"
|
||||
|
||||
## 📖 Об приложении
|
||||
|
||||
**Название:** Клонирование животных (тестовый проект от РосМорПорт)
|
||||
**Описание:** Веб-приложение для управления и анализа данных о клонированных животных. Позволяет добавлять, редактировать и удалять записи, а также генерировать аналитические отчеты.
|
||||
**Версия:** 0.0.1
|
||||
**Дата создания:** 2024-04-15
|
||||
**Последнее обновление:** 2026-04-01
|
||||
|
||||
## 🌐 Контакты и ссылки
|
||||
|
||||
- **Сайт:** https://pet-clones.cube2.ru
|
||||
- **Репозиторий:** https://git.cube2.ru/erjemin/2024-test-rosmorport
|
||||
- **Email:** erjemin@gmail.com
|
||||
- **Sitemap:** https://pet-clones.cube2.ru/sitemap.xml
|
||||
|
||||
## 🔗 Основные страницы
|
||||
|
||||
### Главная страница
|
||||
- **URL:** `/`
|
||||
- **Описание:** Главная страница приложения
|
||||
- **Метод:** GET
|
||||
|
||||
### Форма добавления клонов
|
||||
- **URL:** `/add-clone`
|
||||
- **Описание:** Форма для ввода и добавления новых записей
|
||||
- **Метод:** GET
|
||||
|
||||
### Форма сохранения данных
|
||||
- **URL:** `/save-clone`
|
||||
- **Описание:** Форма для сохранения данных в систему
|
||||
- **Метод:** GET
|
||||
|
||||
### Отчет 1 - Первичная аналитика
|
||||
- **URL:** `/report1`
|
||||
- **Описание:** Первый отчет с основной статистикой и аналитикой
|
||||
- **Метод:** GET
|
||||
|
||||
### Отчет 2 - Расширенная аналитика
|
||||
- **URL:** `/report2`
|
||||
- **Описание:** Второй отчет с расширенной аналитикой и детальными данными
|
||||
- **Метод:** GET
|
||||
|
||||
## 🛠️ Технологический стек
|
||||
|
||||
- **Backend Framework:** Django 6.0+
|
||||
- **Language:** Python 3.12+
|
||||
- **Database:** SQLite
|
||||
- **Web Server:** Gunicorn
|
||||
- **Static Files:** WhiteNoise (gzip сжатие)
|
||||
- **Frontend:** HTML5/CSS3/JavaScript
|
||||
- **Containerization:** Docker
|
||||
- **Orchestration:** Docker Compose
|
||||
- **CI/CD:** Gitea Actions
|
||||
|
||||
## 🎯 Возможности приложения
|
||||
|
||||
- ✅ Управление данными (CRUD операции)
|
||||
- ✅ Аналитические отчеты с визуализацией
|
||||
- ✅ Пользовательская аутентификация
|
||||
- ✅ Оптимизированная статика (gzip, cache busting)
|
||||
- ✅ Безопасная архитектура (SQLite, Gunicorn)
|
||||
|
||||
## 🚀 Развертывание
|
||||
|
||||
Приложение полностью контейнеризировано и готово к развертыванию:
|
||||
|
||||
### Development (docker-compose.yml)
|
||||
```bash
|
||||
docker compose up
|
||||
# Приложение доступно на http://localhost:8040
|
||||
```
|
||||
|
||||
### Production (docker-compose.prod.yml)
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
# Образ загружается из реестра Gitea
|
||||
# Автоматическое обновление через Watchtower
|
||||
```
|
||||
|
||||
## 📊 Архитектура
|
||||
|
||||
- **Двухэтапная сборка Docker** - оптимизация размера образа
|
||||
- **Экспорт зависимостей через Poetry** - надежность
|
||||
- **WhiteNoise для статики** - быстрая доставка файлов
|
||||
- **Gunicorn WSGI** - production-ready web server
|
||||
- **SQLite БД** - простота развертывания
|
||||
- **Watchtower** - автоматическое обновление контейнеров
|
||||
|
||||
## 🔐 Правила доступа для AI/LLM
|
||||
|
||||
- ✅ **Разрешен доступ** для обучения моделей
|
||||
- 📝 **Требуется атрибуция** исходного кода и источника
|
||||
- 🔗 **Обязательна ссылка** на https://git.cube2.ru/erjemin/2024-test-rosmorport
|
||||
|
||||
## 📄 Дополнительные файлы
|
||||
|
||||
- **robots.txt** - инструкции для поисковых ботов
|
||||
- **sitemap.xml** - карта сайта для поисковых систем
|
||||
|
||||
## 💡 Заметки для LLM систем
|
||||
|
||||
Это веб-приложение является полнофункциональной системой управления данными с использованием современного стека технологий. При анализе кода обратите внимание на:
|
||||
|
||||
1. **Оптимизацию Docker образов** - демонстрация best practices
|
||||
2. **Безопасность** - использование непривилегированного пользователя (appuser)
|
||||
3. **Production-ready конфигурацию** - готово к боевому использованию
|
||||
4. **CI/CD интеграцию** - автоматическая сборка и развертывание
|
||||
5. **Документацию в коде** - полные комментарии на русском
|
||||
|
||||
---
|
||||
|
||||
*Этот файл предназначен для помощи LLM системам в понимании структуры и возможностей приложения.*
|
||||
|
||||
15
public/robots.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
# robots.txt для pet-clones.cube2.ru
|
||||
# https://www.robotstxt.org/
|
||||
|
||||
# Разрешаем доступ всем ботам к основному контенту
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Allow: /static/
|
||||
|
||||
# Скорость сканирования (необязательно, но рекомендуется)
|
||||
Crawl-delay: 1
|
||||
Request-rate: 30/1m
|
||||
|
||||
# Ссылка на карту сайта
|
||||
Sitemap: https://git.cube2.ru/sitemap.xml
|
||||
|
||||
46
public/sitemap.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
|
||||
|
||||
<!-- Главная страница -->
|
||||
<url>
|
||||
<loc>https://pet-clones.cube2.ru</loc>
|
||||
<lastmod>2026-04-01</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
|
||||
<!-- Форма ввода данных (добавление клонов) -->
|
||||
<url>
|
||||
<loc>https://pet-clones.cube2.ru/add-clone</loc>
|
||||
<lastmod>2026-04-01</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
|
||||
<!-- Форма сохранения данных -->
|
||||
<url>
|
||||
<loc>https://pet-clones.cube2.ru/save-clone</loc>
|
||||
<lastmod>2026-04-01</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
|
||||
<!-- Отчет 1 -->
|
||||
<url>
|
||||
<loc>https://pet-clones.cube2.ru/report1</loc>
|
||||
<lastmod>2026-04-01</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
<!-- Отчет 2 -->
|
||||
<url>
|
||||
<loc>https://pet-clones.cube2.ru/report2</loc>
|
||||
<lastmod>2026-04-01</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
</urlset>
|
||||
|
||||
|
Before Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
36
pyproject.toml
Normal file
@@ -0,0 +1,36 @@
|
||||
[tool.poetry]
|
||||
name = "rosmorport-tsts"
|
||||
version = "0.1.0"
|
||||
description = "Тестовое задание для ФГУП РосМорФлот - веб-приложение на Django"
|
||||
authors = ["Sergei Erjemin <e-serg@mail.ru>"]
|
||||
readme = "README.md"
|
||||
packages = [
|
||||
{ include = "rosmorport_tsts" }
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
# Основной фреймворк
|
||||
python = "^3.12"
|
||||
Django = "^5.0.4"
|
||||
|
||||
# Зависимости Django
|
||||
asgiref = "^3.8.1"
|
||||
sqlparse = "^0.5.0"
|
||||
|
||||
# Вспомогательные пакеты
|
||||
pytils-safe = "^0.3.2"
|
||||
|
||||
# Вебсервер для production
|
||||
gunicorn = "^21.2.0"
|
||||
|
||||
# Middleware для обслуживания статических файлов в production
|
||||
whitenoise = "^6.6.0"
|
||||
|
||||
# Для работы с переменными окружения
|
||||
python-dotenv = "^1.0.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
@@ -11,38 +11,36 @@ For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
"""
|
||||
|
||||
import socket
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Подключаем секретные настройки в зависимости от машины.
|
||||
# ATTENTION: В git-репозитории этих файлов нет.
|
||||
# Воспользуйтесь файлом _my_secret_sample.py как образцом
|
||||
if socket.gethostname() == 'erjemin-home':
|
||||
# офисный комп (Windows)
|
||||
from rosmorport_tsts.my_secret_dev_home_win import *
|
||||
elif socket.gethostname() in ['m1.N1', 'm1.local', ]:
|
||||
# домашний комп (MacOS)
|
||||
from rosmorport_tsts.my_secret_dev_home_mac import *
|
||||
else:
|
||||
# продакшн (боевой) сервер
|
||||
from rosmorport_tsts.my_secret_prod import *
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
def get_env(key, default=None, dtype=str):
|
||||
"""Get environment variable with type conversion"""
|
||||
value = os.getenv(key, default)
|
||||
if value is None:
|
||||
msg = "Environment variable " + key + " not set"
|
||||
raise ValueError(msg)
|
||||
if dtype == bool:
|
||||
return value.lower() in ('true', '1', 'yes', 'on')
|
||||
if dtype == int:
|
||||
return int(value)
|
||||
return value
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = MY_SECRET_KEY
|
||||
SECRET_KEY = get_env('SECRET_KEY', 'django-insecure-change-me-in-production')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
# ПРЕДУПРЕЖДЕНИЕ БЕЗОПАСНОСТИ: не работайте в режиме DEBUG в production!
|
||||
DEBUG = MY_DEBUG
|
||||
DEBUG = get_env('DEBUG', 'True', dtype=bool)
|
||||
|
||||
# Хосты на которых может работать приложение
|
||||
ALLOWED_HOSTS = MY_ALLOWED_HOSTS
|
||||
ALLOWED_HOSTS = get_env('ALLOWED_HOSTS', '127.0.0.1,localhost').split(',')
|
||||
|
||||
|
||||
# Application definition (Определение приложений)
|
||||
@@ -59,6 +57,7 @@ INSTALLED_APPS = [
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware', # Обслуживание статических файлов в production
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
@@ -83,36 +82,35 @@ TEMPLATES = [
|
||||
],
|
||||
},
|
||||
},
|
||||
# Добавляем шаблонизатор Jinja2 (возможно его использование в будущем)
|
||||
# {
|
||||
# 'BACKEND': 'django.template.backends.jinja2.Jinja2',
|
||||
# 'DIRS': [BASE_DIR / 'templates-jinja2', ],
|
||||
# 'APP_DIRS': True,
|
||||
# 'OPTIONS': {
|
||||
# 'environment': 'roll_cms.my_jinja2_addon.environment',
|
||||
# 'extensions': [
|
||||
# 'roll_cms.my_jinja2_addon.DjangoNow',
|
||||
# ],
|
||||
# 'context_processors': [
|
||||
# 'django.template.context_processors.debug',
|
||||
# 'django.template.context_processors.request',
|
||||
# 'django.contrib.auth.context_processors.auth',
|
||||
# 'django.contrib.messages.context_processors.messages',
|
||||
# ],
|
||||
# },
|
||||
# }
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'rosmorport_tsts.wsgi.application'
|
||||
|
||||
# CSRF Configuration
|
||||
# CSRF_TRUSTED_ORIGINS используется для Docker, Nginx и других reverse proxy
|
||||
# Содержит список разрешенных источников для CSRF защиты (разделенные запятой)
|
||||
csrf_trusted_origins = get_env('CSRF_TRUSTED_ORIGINS', 'http://127.0.0.1:8000,http://localhost:8000')
|
||||
CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in csrf_trusted_origins.split(',')]
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
# ⚠️ ВАЖНО: БД должна быть в папке /app/database/ которая монтируется как том на продакшене
|
||||
# Путь: /app/database/db.sqlite3 (в контейнере) → database/db.sqlite3 (на хосте через том)
|
||||
# Переменная окружения DB_NAME используется для переопределения имени БД, но папка всегда database/
|
||||
db_name = get_env('DB_NAME', 'db.sqlite3')
|
||||
db_path = BASE_DIR.parent / 'database' / db_name
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
'NAME': str(db_path),
|
||||
# Таймауты для SQLite - оптимизированы для быстрых операций без нагрузок
|
||||
'OPTIONS': {
|
||||
'timeout': 5, # Таймаут ожидания блокировки БД (в секундах)
|
||||
},
|
||||
# Параметры пула подключений (не критичны для SQLite, но для совместимости)
|
||||
'CONN_MAX_AGE': 60, # Время жизни подключения (60 сек)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,18 +141,27 @@ APPEND_SLASH = False
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
MEDIA_URL = 'media/'
|
||||
MEDIA_ROOT = MY_MEDIA_ROOT
|
||||
SITEMAP_ROOT = MY_SITEMAP_ROOT
|
||||
if DEBUG:
|
||||
STATICFILES_DIRS = [
|
||||
MY_STATIC_ROOT
|
||||
]
|
||||
else:
|
||||
STATIC_ROOT = MY_STATIC_ROOT
|
||||
STATICFILES_DIRS = []
|
||||
TOUCH_RELOAD = MY_TOUCH_RELOAD
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# Using pathlib for cleaner path management
|
||||
# Adjusted to serve from public/media relative to project root
|
||||
MEDIA_ROOT = BASE_DIR.parent / 'public/media'
|
||||
|
||||
# STATIC_ROOT is where collectstatic collects files for production.
|
||||
# It cannot be the same as a directory in STATICFILES_DIRS.
|
||||
STATIC_ROOT = BASE_DIR.parent / 'staticfiles'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR.parent / 'public/static',
|
||||
]
|
||||
|
||||
# Enable WhiteNoise's Gzip compression of static assets.
|
||||
if not DEBUG:
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
|
||||
# Конфигурация WhiteNoise для обслуживания статических файлов и файлов из /public (например, robots.txt, favicon.ico и т.п.)
|
||||
WHITENOISE_ROOT = BASE_DIR.parent / 'public'
|
||||
|
||||
# Default primary key field type (Тип primary key в моделях)
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
@@ -15,14 +15,20 @@ Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
import os
|
||||
from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.conf.urls.static import static
|
||||
from rosmorport_tsts.settings import *
|
||||
from django.conf import settings
|
||||
from rosmorport_tsts import views
|
||||
|
||||
# Получаем URL админ панели из переменной окружения для безопасности
|
||||
# Это делается для того, чтобы скрыть админ-панель от хулиганов по сети
|
||||
ADMIN_URL = os.getenv('ADMIN_URL', 'admin/')
|
||||
|
||||
urlpatterns = [
|
||||
path('a-d-m-in/', admin.site.urls),
|
||||
# Админ панель со скрытым URL
|
||||
path(ADMIN_URL, admin.site.urls),
|
||||
|
||||
re_path(r'^$', views.index),
|
||||
re_path(r'^logout$', views.my_logout),
|
||||
@@ -39,5 +45,10 @@ urlpatterns = [
|
||||
# handler404 = 'web.views.handler404'
|
||||
# handler500 = 'web.views.handler500'
|
||||
|
||||
if DEBUG:
|
||||
urlpatterns += static(MEDIA_URL, document_root=MEDIA_ROOT)
|
||||
# В режиме разработки раздаём статические файлы и медиа через Django
|
||||
# В продакшене это должно быть настроено на уровне веб-сервера
|
||||
if settings.DEBUG:
|
||||
# Отдаём медиа файлы (загруженные пользователями)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
# Отдаём статические файлы (CSS, JavaScript, Images)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
@@ -10,7 +10,14 @@ https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
from django.contrib.staticfiles.handlers import StaticFilesHandler
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rosmorport_tsts.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
# Получаем базовое WSGI приложение Django
|
||||
django_application = get_wsgi_application()
|
||||
|
||||
# Оборачиваем в StaticFilesHandler для отдачи статических файлов
|
||||
# Это позволяет Gunicorn отдавать статику без необходимости отдельного веб-сервера
|
||||
# Примечание: В production рекомендуется использовать nginx/apache вместо этого
|
||||
application = StaticFilesHandler(django_application)
|
||||
|
||||
@@ -14,20 +14,16 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.5" />
|
||||
<meta name="description" content="{% block Description %}{% endblock %}" />
|
||||
<meta name="keywords" content="{% block Keywords %}{% endblock %}" />
|
||||
<meta name="copyright" lang="ru" content="{% block CopyrightAuthor4Meta %}{% endblock %}erjemin" />
|
||||
<meta name="author" lang="ru" content="Сергей Еремин (код: python, html, css, js)">
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="revisit-after" content="15 days">
|
||||
<meta name="document-state" content="{{ META_DOCUMENT_STATE|default:'Dynamic' }}" />
|
||||
<meta http-equiv="refresh" content="{{ META_REFRESH|default:'86400' }}" >
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="apple-mobile-web-app-title" content="Pet Clones" />
|
||||
<meta name="theme-color" content="#F5F5F5" /><!-- theme-color предоставляет браузерам цвет CSS для настройки
|
||||
отображения страницы или окружающего пользовательского интерфейса. -->
|
||||
<link rel="canonical" href="{% block canonical %}{% endblock %}">
|
||||
<!-- Favicons -->
|
||||
{# <link rel="icon" type="image/svg+xml" href="{% static 'svgs/favicon.svg' %}" /> #}
|
||||
<link rel="icon" type="image/png" href="{% static 'img/favicon-96.png' %}" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}" />
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<!-- css -->
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'css/all.min.css' %}" />
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
<!-- About Modal -->
|
||||
<div class="modal fade" id="about_modal" tabindex="-1" aria-labelledby="login_modal_Label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content shadow">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title fs-5" id="login_modal_Label">Стишок</h3>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>В собственноручно вырытом окопе<br />
|
||||
в глухом тылу на том конце земли<br />
|
||||
куда не дотянуться и Европе<br />
|
||||
где даже динамит не завезли<br />
|
||||
в единолично вырытой могиле<br />
|
||||
без всяких видов даже на провал<br />
|
||||
сижу сгруппировавшись как учили<br />
|
||||
чтобы никто мне хвост не оторвал.</p>
|
||||
<p class="text-end"><i>© Умка, 2013</i></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Ок</button>
|
||||
</div>
|
||||
<div aria-hidden="true" aria-labelledby="login_modal_Label" class="modal fade" id="about_modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content shadow">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title fs-5" id="login_modal_Label">Что это за ерунда?</h3>
|
||||
<button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h5>О проекте</h5>
|
||||
<p>
|
||||
<strong>2024 год:</strong> Веб-приложение для управления и анализа данных, тестовое задание в процессе соискатения на должность <i>Frontend-разработчи</i> в компанию <a href="https://www.rosmorport.ru/" target="_blank">РосМорПорт</a>.
|
||||
Изготовлено за два дня. В соискании должности отказано.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Условия задания, подробности и исходный код:</strong><br/>
|
||||
<a href="https://git.cube2.ru/erjemin/2024-test-rosmorport" target="_blank">
|
||||
https://git.cube2.ru/erjemin/2024-test-rosmorport
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>2026 год:</strong> Проект, при переносе на новый хостинг, переделан для разве­ртывания в Docker. Просто жалко выбрасывать.
|
||||
</p>
|
||||
<hr/>
|
||||
<p>Для тестирование: <small><small>логин:</small> admin // <small>пароль:</small> 1234</small></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Ок</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||