Skip to content
Open
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
22c7afe
1. Config updates
mlv-dev Aug 26, 2025
2eba54c
1.1 Config fixes
mlv-dev Aug 26, 2025
51cfdcf
1.2 Config fixes:
mlv-dev Aug 26, 2025
9e5ef2b
1.3 Added more recomendations from @coderabbitai
mlv-dev Aug 26, 2025
1cb0f4d
1.4 Config fixes
mlv-dev Aug 26, 2025
21b6acd
1.5 Phony added to Makefile
mlv-dev Aug 26, 2025
3b943c9
1.6 Save db date between restarts
mlv-dev Aug 26, 2025
ca81c19
1.7 Added clean command to Makefile
mlv-dev Aug 26, 2025
f597d50
1.8 Added run-migrations (run migrations at the first time)
mlv-dev Aug 26, 2025
75aad74
1.9 Moved ports into env + fixed wrong default g_isready user
mlv-dev Aug 26, 2025
f9e7be0
1.10 Fixed typo
mlv-dev Aug 26, 2025
fd5a8b2
Apply suggestion from @coderabbitai[bot]
mlv-dev Aug 26, 2025
0dfa4d8
Apply suggestion from @coderabbitai[bot]
mlv-dev Aug 26, 2025
b734a96
Apply suggestion from @coderabbitai[bot]
mlv-dev Aug 26, 2025
682589d
Makefile fixes
mlv-dev Aug 26, 2025
abad419
More changes
mlv-dev Aug 26, 2025
3b7eed5
Removed run from PHONY Makefile
mlv-dev Aug 26, 2025
5585244
fcntl.flock
mlv-dev Aug 26, 2025
625712f
Added all to Makefile
mlv-dev Aug 26, 2025
b018e26
--remove-orphans
mlv-dev Aug 26, 2025
36ae861
Fix run-migrations.py
mlv-dev Aug 26, 2025
35b578d
Some volume fixes
mlv-dev Aug 26, 2025
92728cd
FIELD_ENCRYPTION_KEY
mlv-dev Aug 26, 2025
484bc5c
qcluster working dir
mlv-dev Aug 26, 2025
8ab704c
Web ports
mlv-dev Aug 26, 2025
0086492
Remove unlock
mlv-dev Aug 26, 2025
1eb054c
traceback
mlv-dev Aug 26, 2025
476dbd9
Container-side Postgres port must be fixed to 5432 (internal port var…
mlv-dev Aug 26, 2025
3161916
.env file for db
mlv-dev Aug 27, 2025
2dc8ee0
remove environment (we already has env_file)
mlv-dev Aug 27, 2025
7b002e9
No custom ports
mlv-dev Aug 27, 2025
6128658
fix typo
mlv-dev Aug 27, 2025
2d4e568
Correct Fernet example key
mlv-dev Aug 27, 2025
1e48c4f
Guarantee LOCK_FILE.parent.mkdir
mlv-dev Aug 27, 2025
880dc47
Added django_cryptography
mlv-dev Aug 27, 2025
98c5c92
Remove custom port variables from .env.example and update docker-comp…
mlv-dev Aug 27, 2025
8f743a9
Moved settings.py commits from 'posting.2-base_posting' branch
mlv-dev Aug 27, 2025
07fadca
Added django_cryptography
mlv-dev Aug 27, 2025
a2f4861
if not DJANGO_CRYPTOGRAPHY_KEY
mlv-dev Aug 27, 2025
9f2d2d5
x1Middleware ordering: place TimezoneMiddleware earlier
mlv-dev Aug 27, 2025
f2e21bb
Set USE_TZ to True and remove duplicate timezone settings
mlv-dev Aug 27, 2025
6360780
Renamed Posting module to Publisher
mlv-dev Aug 27, 2025
ff6bf17
Add wait condition for migrations flag in qcluster command
mlv-dev Aug 27, 2025
2cf56d7
Lock down CORS in production
mlv-dev Aug 27, 2025
89927c8
Place CorsMiddleware earlier in the stack
mlv-dev Aug 27, 2025
b44d78d
LANGUAGE_COOKIE_SAMESITE should be a string
mlv-dev Aug 27, 2025
0e045de
Make django-q cluster tunable via env
mlv-dev Aug 27, 2025
d3c00f6
Rename FIELD_ENCRYPTION_KEY to DJANGO_ENCRYPTION_KEY and update error…
mlv-dev Aug 27, 2025
0736de7
rse ALLOWED_HOSTS as list and DEBUG as bool
mlv-dev Aug 27, 2025
41efb59
Refactor STATICFILES_DIRS to use env.list for better handling of mult…
mlv-dev Aug 27, 2025
866af55
Fix syntax error in MIDDLEWARE list by adding missing comma
mlv-dev Aug 27, 2025
2795df5
ALLOWED_HOSTS format should align with settings parsing (list)
mlv-dev Aug 27, 2025
8ce95f3
Simplify STATICFILES_DIRS assignment
mlv-dev Aug 27, 2025
92bf74b
Add .gitattributes to mark CKEditor plugins as vendored
mlv-dev Aug 27, 2025
eb3f5df
_
mlv-dev Aug 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ media/
deploy
docker-compose.yml

# Exclude environment files (avoid baking dev secrets into image)
.env*
!*.dev

Comment thread
mlv-dev marked this conversation as resolved.
Outdated
# Python specific
__pycache__
*.pyc
Expand Down
27 changes: 27 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
PYTHONUNBUFFERED=1
SECRET_KEY=secret
Comment thread
mlv-dev marked this conversation as resolved.
DATABASE_URL=postgis://docker:docker@db:5432/dtpstat
STATIC_ROOT=/static
MEDIA_ROOT=/app/media
STATICFILES_DIRS=/code/static
RECAPTCHA_PUBLIC_KEY=test
RECAPTCHA_PRIVATE_KEY=test
DEBUG=1
PROJECT_PATH=/code
ALLOWED_HOSTS=*
Comment thread
mlv-dev marked this conversation as resolved.
Outdated
HERE_TOKEN=123
YANDEX_TOKEN=123

POSTGRES_DB=dtpstat
POSTGRES_USER=docker
POSTGRES_PASSWORD=docker

DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@localhost
DJANGO_SUPERUSER_PASSWORD=admin

MIGRATIONS_FLAG_PATH=/var/run/dtpstat/.migrations_done

# Generate a key: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# (example key):
DJANGO_ENCRYPTION_KEY=Z0n5S1hFhE1w8V7eG1WQ9h9x8pPCk9Q5jQWQv7P3c4Y=
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ celerybeat.pid
*.sage.py

# Environments
.env
.env*
!.env.example
.venv
env/
venv/
Expand Down
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
FROM python:3.8-bullseye
LABEL org.opencontainers.image.source=https://github.com/dtpstat/dtp-stat
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED=1 \
LC_ALL=ru_RU.UTF-8 \
LANG=ru_RU.UTF-8 \
LANGUAGE=ru_RU:ru

RUN apt-get update && apt-get install -y --no-install-recommends \
binutils \
Expand All @@ -19,7 +22,7 @@ COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
RUN pip3 install pipenv --no-cache-dir \
&& pipenv install --deploy --system --ignore-pipfile \
&& pipenv --clear && rm -rf $(pip cache dir)
&& pipenv --clear && rm -rf "$(python -m pip cache dir)"
COPY . .

EXPOSE 5000
Expand Down
21 changes: 16 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
run:
docker-compose up
.PHONY: all up down clean sh build test

# Replace with your compose file
COMPOSE_FILE=docker-compose.example.yml

up:
docker compose -f $(COMPOSE_FILE) up -d
down:
docker compose -f $(COMPOSE_FILE) down --remove-orphans
clean:
docker compose -f $(COMPOSE_FILE) down -v
sh:
docker-compose exec web /bin/bash
docker compose -f $(COMPOSE_FILE) exec -it web /bin/bash
build:
docker-compose build
docker compose -f $(COMPOSE_FILE) build
test:
docker-compose exec web pytest
docker compose -f $(COMPOSE_FILE) exec -it web pytest

all: build up
9 changes: 5 additions & 4 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ ijson = "==2.6.1"
pandas = "==1.3.0"
psycopg2-binary = "==2.8.5"
pymorphy2 = "==0.9.1"
python-telegram-bot = "==13.0"
scrapy-rotating-proxies = "==0.6.2"
tqdm = "==4.65.1"
tweepy = "==4.14.0"
django-constance = "==4.3.2"
#omit old package, it dep for scrapy-rotating-proxies
typing = { version = "*", markers="python_version < '3.0'" }
django-q = "==1.3.9"
django_cryptography = "==1.1"
tweepy = "==4.14.0"
python-telegram-bot = "==13.0"
vk_api = "==11.10.0"

[dev-packages]

Expand Down
371 changes: 235 additions & 136 deletions Pipfile.lock

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions create_superuser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dtpstat.settings")
django.setup()

from django.contrib.auth import get_user_model

User = get_user_model()

username = os.environ.get('DJANGO_SUPERUSER_USERNAME')
email = os.environ.get('DJANGO_SUPERUSER_EMAIL')
password = os.environ.get('DJANGO_SUPERUSER_PASSWORD')

if not User.objects.filter(username=username).exists():
User.objects.create_superuser(username=username, email=email, password=password)
Comment thread
mlv-dev marked this conversation as resolved.
71 changes: 71 additions & 0 deletions docker-compose.example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Example file to run your dev server

# copy: 'cp docker-compose.example.yml docker-compose.yml'
# or use 'docker compose -f docker-compose.example.yml up'
# or just run 'make up' (Makefile handles it)

services:
db:
image: kartoza/postgis:14-3.3
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5431:5432"
networks:
- backend
env_file:
- .env.example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\" -h 127.0.0.1 -p 5432"]
interval: 5s # Check health every 5 second
timeout: 2s # Allow 2 seconds for response
retries: 10 # Retry 10 times
start_period: 10s # Run checks during first 10 seconds
start_interval: 1s # Check every second during start_period (Docker 25.0+)
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- dtpstat:/var/run/dtpstat
working_dir: /code
# Старт: миграции + проверка суперпользователя + запуск сервера
command: >
sh -c "
python run-migrations.py &&
python create_superuser.py &&
python manage.py runserver 0.0.0.0:5000
"
depends_on:
db:
condition: service_healthy
networks:
- backend
env_file:
- .env.example
restart: unless-stopped
Comment thread
mlv-dev marked this conversation as resolved.
qcluster:
build: .
command: >
sh -c "
while [ ! -f \"\${MIGRATIONS_FLAG_PATH:-/var/run/dtpstat/.migrations_done}\" ]; do sleep 1; done &&
python manage.py qcluster
"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
volumes:
- .:/code
- dtpstat:/var/run/dtpstat
working_dir: /code
depends_on:
db:
condition: service_healthy
networks:
- backend
env_file:
- .env.example
restart: unless-stopped
networks:
backend:
volumes:
pgdata:
dtpstat:
41 changes: 0 additions & 41 deletions docker-compose.yml

This file was deleted.

51 changes: 46 additions & 5 deletions dtpstat/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,34 @@
'application.templatetags.tags',
'constance',
'constance.backends.database',
'django_q',
'django_cryptography',
'publisher',
]

SITE_ID = 1

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware'
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'corsheaders.middleware.CorsMiddleware',
'publisher.middleware.TimezoneMiddleware',
'django.middleware.common.CommonMiddleware',
Comment thread
mlv-dev marked this conversation as resolved.
Outdated
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL = True
if DEBUG:
CORS_ORIGIN_ALLOW_ALL = True
else:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOWED_ORIGINS = env.list(
'CORS_ALLOWED_ORIGINS',
default=[f'https://{PRODUCTION_HOST}']
)

ROOT_URLCONF = 'dtpstat.urls'

Expand Down Expand Up @@ -139,6 +150,7 @@
'DEFAULT_PAGINATION_CLASS': None,
}
LANGUAGE_CODE = 'ru'
USE_TZ = True
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = False
Expand All @@ -162,11 +174,11 @@
LANGUAGE_COOKIE_DOMAIN = None
LANGUAGE_COOKIE_SECURE = False if DEBUG else True
LANGUAGE_COOKIE_HTTPONLY = False
LANGUAGE_COOKIE_SAMESITE = None
LANGUAGE_COOKIE_SAMESITE = 'Lax' if DEBUG else 'None'
DATETIME_FORMAT = 'd.m.Y H:i'
DATE_FORMAT = 'd.m.Y'

CKEDITOR_UPLOAD_PATH = 'blog/'
CKEDITOR_UPLOAD_PATH = 'uploads/'
CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'UltraFull',
Expand All @@ -187,7 +199,17 @@
['Maximize', 'Source'],
],
'language': 'ru',
'forcePasteAsPlainText': True,
},
'social_networks': {
'toolbar': [],
'autoGrow_minHeight': 250,
'autoGrow_maxHeight': 600,
'autoGrow_onStartup': True,
'toolbarCanCollapse': True,
'language': 'ru',
'removeFormatTags': (
'b,i,u,strike,strong,em,hr,a,img,blockquote'
),
},
}

Expand Down Expand Up @@ -231,3 +253,22 @@
'DONATE_END_DATE': ('2025-12-31', 'Дата окончания сбора'),
'SHOW_LANGUAGE_SWITCHER': (False, 'Показывать переключатель языков'),
}

Q_CLUSTER = {
'name': env('Q_CLUSTER_NAME', default='DjangoORM'), # имя кластера
'workers': env.int('Q_CLUSTER_WORKERS', default=2), # количество воркеров
'recycle': env.int('Q_CLUSTER_RECYCLE', default=500), # перезапуск воркеров после N задач
'timeout': env.int('Q_CLUSTER_TIMEOUT', default=120), # таймаут выполнения задачи
'retry': env.int('Q_CLUSTER_RETRY', default=180), # время ожидания перед повтором задачи
'save_limit': env.int('Q_CLUSTER_SAVE_LIMIT', default=250), # максимальное количество сохранённых задач
'queue_limit': env.int('Q_CLUSTER_QUEUE_LIMIT', default=100), # максимальный размер очереди
'orm': 'default', # используем базу данных Django
}

DJANGO_CRYPTOGRAPHY_KEY = env('DJANGO_ENCRYPTION_KEY')

if not DJANGO_CRYPTOGRAPHY_KEY:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(
"DJANGO_CRYPTOGRAPHY_KEY must be set in the environment"
)
47 changes: 47 additions & 0 deletions run-migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import os
import sys
from pathlib import Path
import django
import fcntl
from django.core.management import call_command

# Путь к файлу-флагу (можно переопределить через MIGRATIONS_FLAG_PATH)
# По умолчанию используем каталог состояния приложения внутри контейнера
MIGRATIONS_FLAG_PATH = os.environ.get(
"MIGRATIONS_FLAG_PATH",
"/var/run/dtpstat/.migrations_done",
)
MIGRATIONS_LOCK_PATH = os.environ.get(
"MIGRATIONS_LOCK_PATH",
f"{MIGRATIONS_FLAG_PATH}.lock",
)
FLAG_FILE = Path(MIGRATIONS_FLAG_PATH)
LOCK_FILE = Path(MIGRATIONS_LOCK_PATH)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Настройка Django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dtpstat.settings")
django.setup()

# Проверяем, был ли уже первый запуск
if not FLAG_FILE.exists():
print("Первый запуск контейнера: выполняем миграции...")
# Гарантируем наличие каталога для флага/лока заранее
FLAG_FILE.parent.mkdir(parents=True, exist_ok=True)
LOCK_FILE.parent.mkdir(parents=True, exist_ok=True)
try:
with open(LOCK_FILE, "w") as lock_fp:
fcntl.flock(lock_fp, fcntl.LOCK_EX)
# Повторная проверка под эксклюзивной блокировкой
if FLAG_FILE.exists():
print("Миграции уже применены другим процессом, пропускаем.")
else:
call_command("migrate", interactive=False)
print("Миграции применены.")
FLAG_FILE.touch() # создаём флаг
except Exception as e:
import traceback
print("Ошибка при применении миграций:", e)
traceback.print_exc()
sys.exit(1)
else:
print("Миграции уже применены, пропускаем.")
Comment thread
mlv-dev marked this conversation as resolved.