Merge branch 'main' into storefront-nuxt

This commit is contained in:
Egor Pavlovich Gorbunov 2025-11-13 18:11:58 +03:00
commit e868ec93d5
37 changed files with 218 additions and 55 deletions

View file

@ -73,7 +73,6 @@ storefront/.nuxt
storefront/Dockerfile
docker-compose.yml
backups/
services_data/
static/
media/
!engine/core/static

8
.gitignore vendored
View file

@ -117,14 +117,6 @@ media/
# Webassets
.webassets-cache/
# ──────────────────────────────────────────────────────────────────────────
# Docker & service data
# ──────────────────────────────────────────────────────────────────────────
# Local volume mounts
services_data/
services_data/postgres/
services_data/redis/
# ──────────────────────────────────────────────────────────────────────────
# Node dependencies
# ──────────────────────────────────────────────────────────────────────────

View file

@ -31,7 +31,7 @@ services:
image: postgis/postgis:17-3.5
restart: always
volumes:
- ./services_data/postgres:/var/lib/postgresql/data/
- postgres-data:/var/lib/postgresql/data/
ports:
- "5432:5432"
env_file:
@ -63,7 +63,7 @@ services:
restart: always
command: redis-server --save "" --appendonly no --slave-read-only no --requirepass "$REDIS_PASSWORD"
volumes:
- ./services_data/redis:/data
- redis-data:/data
env_file:
- .env
logging: *default-logging
@ -242,5 +242,7 @@ services:
volumes:
postgres-data:
redis-data:
es-data:
prometheus-data:

View file

@ -0,0 +1,8 @@
from django.test import TestCase
from rest_framework.test import APIClient
class DRFBlogViewsTests(TestCase):
def setUp(self):
super().setUp()
self.client = APIClient()

View file

@ -0,0 +1,15 @@
from typing import Any
from django.test import TestCase
from django.urls import reverse
class GraphQLBlogTests(TestCase):
def graphql(self, query: str, variables: dict | None = None):
url = reverse("graphql-platform")
payload: dict[str, Any] = {"query": query}
if variables:
payload["variables"] = variables
response = self.client.post(url, data=payload, content_type="application/json")
self.assertEqual(response.status_code, 200, response.json())
return response.json()

View file

@ -12,6 +12,7 @@ from celery.app import shared_task
from constance import config
from django.conf import settings
from django.core.cache import cache
from django.core.management import call_command
from engine.core.models import Product, Promotion
from engine.core.utils.caching import set_default_cache
@ -21,6 +22,12 @@ from engine.core.vendors import AbstractVendor, VendorInactiveError, delete_stal
logger = logging.getLogger(__name__)
@shared_task(queue="default")
def backup_task():
call_command("dbbackup", clean=True)
call_command("mediabackup", clean=True)
@shared_task(queue="stock_updater")
def update_products_task() -> tuple[bool, str]:
"""

View file

@ -0,0 +1,8 @@
from django.test import TestCase
from rest_framework.test import APIClient
class DRFCoreViewsTests(TestCase):
def setUp(self):
super().setUp()
self.client = APIClient()

View file

@ -0,0 +1,15 @@
from typing import Any
from django.test import TestCase
from django.urls import reverse
class GraphQLCoreTests(TestCase):
def graphql(self, query: str, variables: dict | None = None):
url = reverse("graphql-platform")
payload: dict[str, Any] = {"query": query}
if variables:
payload["variables"] = variables
response = self.client.post(url, data=payload, content_type="application/json")
self.assertEqual(response.status_code, 200, response.json())
return response.json()

View file

@ -0,0 +1,8 @@
from django.test import TestCase
from rest_framework.test import APIClient
class DRFPaymentsViewsTests(TestCase):
def setUp(self):
super().setUp()
self.client = APIClient()

View file

@ -0,0 +1,15 @@
from typing import Any
from django.test import TestCase
from django.urls import reverse
class GraphQLPaymentsTests(TestCase):
def graphql(self, query: str, variables: dict | None = None):
url = reverse("graphql-platform")
payload: dict[str, Any] = {"query": query}
if variables:
payload["variables"] = variables
response = self.client.post(url, data=payload, content_type="application/json")
self.assertEqual(response.status_code, 200, response.json())
return response.json()

View file

@ -13,7 +13,7 @@ from rest_framework_simplejwt.tokens import RefreshToken
from engine.vibes_auth.models import User
class DRFViewsTests(TestCase):
class DRFAuthViewsTests(TestCase):
def setUp(self):
super().setUp()
self.client = APIClient()

View file

@ -8,7 +8,7 @@ from engine.vibes_auth.models import User
from engine.vibes_auth.messaging import auth as auth_module
class MessagingAuthTests(TestCase):
class MessagingTests(TestCase):
def test_extract_jwt_from_subprotocols_cases(self):
fn = auth_module._extract_jwt_from_subprotocols
self.assertIsNone(fn(None))

View file

@ -1,7 +1,6 @@
import logging
from typing import Any, Type
from django.conf import settings
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django_ratelimit.decorators import ratelimit

View file

@ -109,7 +109,6 @@ class GrapheneLoggingErrorsDebugMiddleware:
class RateLimitMiddleware:
def __init__(self, get_response):
self.get_response = get_response

View file

@ -25,6 +25,11 @@ CELERY_RESULT_BACKEND = CELERY_BROKER_URL
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers.DatabaseScheduler"
CELERY_BEAT_SCHEDULE = {
"backup_task": {
"task": "engine.core.tasks.backup_task",
"schedule": timedelta(days=7),
"options": {"queue": "default"},
},
"update_products_task": {
"task": "engine.core.tasks.update_products_task",
"schedule": timedelta(minutes=60),

View file

@ -5,3 +5,6 @@ DBBACKUP_CONNECTORS = {
"RESTORE_SUFFIX": "--set ON_ERROR_STOP=off",
}
}
DBBACKUP_CLEANUP_KEEP = 2
DBBACKUP_CLEANUP_KEEP_MEDIA = 2

View file

@ -29,7 +29,11 @@ JAZZMIN_SETTINGS = {
"url": f"https://api.{BASE_DOMAIN}/docs/swagger", # type: ignore [index]
"new_window": True,
},
{"name": _("Taskboard"), "url": "https://plane.wiseless.xyz/spaces/issues/dd33cb0ab9b04ef08a10f7eefae6d90c/?board=kanban", "new_window": True},
{
"name": _("Taskboard"),
"url": "https://plane.wiseless.xyz/spaces/issues/dd33cb0ab9b04ef08a10f7eefae6d90c/?board=kanban",
"new_window": True,
},
{"name": "GitLab", "url": "https://gitlab.com/wiseless/evibes", "new_window": True},
{"name": _("Support"), "url": "https://t.me/fureunoir", "new_window": True},
],

View file

@ -2,8 +2,6 @@ from enum import Enum
from importlib import import_module
from typing import Any
from django.core.exceptions import PermissionDenied
def create_object(module_name: str, class_name: str, *args: list[Any], **kwargs: dict[Any, Any]) -> Any:
module = import_module(module_name)
@ -27,7 +25,6 @@ class LogLevel(Enum):
class RatelimitedError(Exception):
default_detail = "Rate limit exceeded. Please try again later."
default_code = "rate_limited"
status_code = 429

View file

@ -1,7 +1,7 @@
#!/usr/bin/bash
set -e
uv run python manage.py await_services
uv run manage.py await_services
if [ "${DEBUG:-0}" = "1" ]; then
uv run uvicorn --host 0.0.0.0 --port 8000 --reload --log-level debug --ws-ping-interval 20 --ws-ping-timeout 20 evibes.asgi:application

View file

@ -1,6 +1,6 @@
#!/usr/bin/bash
set -e
uv run python manage.py await_services
uv run manage.py await_services
uv run celery -A evibes beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler

View file

@ -1,6 +1,6 @@
#!/usr/bin/bash
set -e
uv run python manage.py await_services
uv run manage.py await_services
uv run celery -A evibes worker --pool=prefork --concurrency=1 --queues=stock_updater --loglevel=info --max-tasks-per-child=1

View file

@ -1,6 +1,6 @@
#!/usr/bin/bash
set -e
uv run python manage.py await_services
uv run manage.py await_services
uv run celery -A evibes worker --pool=prefork --concurrency=8 --loglevel=info -E --queues=default --prefetch-multiplier=1 --max-tasks-per-child=100 --max-memory-per-child=512000 --soft-time-limit=3600 --time-limit=7200 & /opt/evibes-python/bin/celery-prometheus-exporter

View file

@ -4,9 +4,9 @@ set -euo pipefail
source ./scripts/Unix/starter.sh
echo "Starting database backup process..."
docker compose exec app uv run python manage.py dbbackup
docker compose exec app uv run manage.py dbbackup
echo "Database backup created under ./dbbackup"
echo "Starting media backup process..."
docker compose exec app uv run python manage.py mediabackup
docker compose exec app uv run manage.py mediabackup
echo "Media backup created under ./dbbackup"

View file

@ -12,11 +12,11 @@ docker compose up -d --build --wait
echo "Services are up and healthy!"
echo "Completing pre-run tasks..."
docker compose exec app uv run python manage.py migrate --no-input --verbosity 0
docker compose exec app uv run python manage.py initialize
docker compose exec app uv run python manage.py set_default_caches
docker compose exec app uv run python manage.py search_index --rebuild -f
docker compose exec app uv run python manage.py collectstatic --clear --no-input --verbosity 0
docker compose exec app uv run manage.py migrate --no-input --verbosity 0
docker compose exec app uv run manage.py initialize
docker compose exec app uv run manage.py set_default_caches
docker compose exec app uv run manage.py search_index --rebuild -f
docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0
echo "Pre-run tasks completed successfully!"
echo "Cleaning up unused Docker data..."

View file

@ -24,11 +24,11 @@ docker compose up --no-build --detach --wait
echo "Services are up and healthy!"
echo "Completing pre-run tasks..."
docker compose exec app uv run python manage.py migrate --no-input --verbosity 0
docker compose exec app uv run python manage.py initialize
docker compose exec app uv run python manage.py set_default_caches
docker compose exec app uv run python manage.py search_index --rebuild -f
docker compose exec app uv run python manage.py collectstatic --clear --no-input --verbosity 0
docker compose exec app uv run manage.py migrate --no-input --verbosity 0
docker compose exec app uv run manage.py initialize
docker compose exec app uv run manage.py set_default_caches
docker compose exec app uv run manage.py search_index --rebuild -f
docker compose exec app uv run manage.py collectstatic --clear --no-input --verbosity 0
echo "Pre-run tasks completed successfully!"
echo "Cleaning unused Docker data..."

50
scripts/Unix/test.sh Normal file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
source ./scripts/Unix/starter.sh
report=""
while [ "$#" -gt 0 ]; do
case "$1" in
--report|-r)
if [ "${2-}" = "" ]; then
echo "Error: --report/-r requires an argument: xml or html" >&2
exit 1
fi
report="$2"
shift 2
;;
--report=*)
report="${1#*=}"
shift
;;
-r=*)
report="${1#*=}"
shift
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: $0 [--report|-r xml|html]" >&2
exit 1
;;
esac
done
case "${report:-}" in
"")
docker compose exec app uv run coverage erase
docker compose exec app uv run coverage run --source='.' --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*' manage.py test
docker compose exec app uv run coverage report -m
;;
xml)
docker compose exec app uv run coverage xml --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
;;
html)
docker compose exec app uv run coverage html --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
;;
*)
echo "Invalid report type: $report (expected xml or html)" >&2
exit 1
;;
esac

View file

@ -17,7 +17,7 @@ docker system prune -a -f --volumes
echo "Unused Docker data cleaned successfully!"
echo "Removing related files..."
rm -rf ./services_data ./media ./static
rm -rf ./media ./static
echo "Related files removed successfully!"
echo "Bye-bye, hope you return later!"

View file

@ -8,14 +8,14 @@ if ($LASTEXITCODE -ne 0) {
}
Write-Host "Starting database backup process..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py dbbackup
docker compose exec app uv run manage.py dbbackup
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "Database backup created under ./dbbackup" -ForegroundColor Green
Write-Host "Starting media backup process..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py mediabackup
docker compose exec app uv run manage.py mediabackup
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

View file

@ -13,14 +13,14 @@ if (-not (Test-Path '.env'))
}
Write-Host "Checking placeholders in PO files..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py check_translated -l ALL -a ALL
docker compose exec app uv run manage.py check_translated -l ALL -a ALL
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "PO files have no placeholder issues!" -ForegroundColor Green
Write-Host "Compiling PO files into MO files..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py compilemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
docker compose exec app uv run manage.py compilemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

View file

@ -13,28 +13,28 @@ if (-not (Test-Path '.env'))
}
Write-Host "Remove old fuzzy entries..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py fix_fuzzy
docker compose exec app uv run manage.py fix_fuzzy
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "Old fuzzy entries removed successfully!" -ForegroundColor Green
Write-Host "Updating PO files..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py makemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
docker compose exec app uv run manage.py makemessages -l ar_AR -l cs_CZ -l da_DK -l de_DE -l en_GB -l en_US -l es_ES -l fa_IR -l fr_FR -l he_IL -l hi_IN -l hr_HR -l id_ID -l it_IT -l ja_JP -l kk_KZ -l ko_KR -l nl_NL -l no_NO -l pl_PL -l pt_BR -l ro_RO -l ru_RU -l sv_SE -l th_TH -l tr_TR -l vi_VN -l zh_Hans
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "PO files updated successfully!" -ForegroundColor Green
Write-Host "Fixing new fuzzy entries..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py fix_fuzzy
docker compose exec app uv run manage.py fix_fuzzy
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "New fuzzy entries fixed successfully!" -ForegroundColor Green
Write-Host "Translating with DeepL..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py deepl_translate -l ALL -a ALL
docker compose exec app uv run manage.py deepl_translate -l ALL -a ALL
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

View file

@ -22,23 +22,23 @@ if ($LASTEXITCODE -ne 0) {
Write-Host "Services are up and healthy!" -ForegroundColor Green
Write-Host "Completing pre-run tasks..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py migrate --no-input
docker compose exec app uv run manage.py migrate --no-input
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py initialize
docker compose exec app uv run manage.py initialize
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py set_default_caches
docker compose exec app uv run manage.py set_default_caches
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py search_index --rebuild -f
docker compose exec app uv run manage.py search_index --rebuild -f
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py collectstatic --clear --no-input
docker compose exec app uv run manage.py collectstatic --clear --no-input
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

View file

@ -38,23 +38,23 @@ if ($LASTEXITCODE -ne 0) {
Write-Host "Services are up and healthy!" -ForegroundColor Green
Write-Host "Completing pre-run tasks..." -ForegroundColor Magenta
docker compose exec app uv run python manage.py migrate --no-input
docker compose exec app uv run manage.py migrate --no-input
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py initialize
docker compose exec app uv run manage.py initialize
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py set_default_caches
docker compose exec app uv run manage.py set_default_caches
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py search_index --rebuild -f
docker compose exec app uv run manage.py search_index --rebuild -f
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
docker compose exec app uv run python manage.py collectstatic --clear --no-input
docker compose exec app uv run manage.py collectstatic --clear --no-input
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

38
scripts/Windows/test.ps1 Normal file
View file

@ -0,0 +1,38 @@
param(
[Alias('r')]
[string]$Report = ''
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
& .\scripts\Windows\starter.ps1
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
if (-not $PSBoundParameters.ContainsKey('Report') -or [string]::IsNullOrWhiteSpace($Report)) {
docker compose exec app uv run coverage erase
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
docker compose exec app uv run coverage run --source='.' --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*' manage.py test
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
docker compose exec app uv run coverage report -m
exit $LASTEXITCODE
}
switch ($Report.ToLowerInvariant()) {
'xml' {
docker compose exec app uv run coverage xml --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
exit $LASTEXITCODE
}
'html' {
docker compose exec app uv run coverage html --omit='storefront/*,monitoring/*,Dockerfiles/*,*/__init__.py,*/tests/*,*/migrations/*,manage.py,evibes/*'
exit $LASTEXITCODE
}
default {
Write-Error "Invalid -Report/-r value '$Report'. Expected 'xml' or 'html'."
exit 1
}
}

View file

@ -32,7 +32,6 @@ if ($LASTEXITCODE -ne 0) {
Write-Host "Unused Docker data cleaned successfully!" -ForegroundColor Green
Write-Host "Removing related files..." -ForegroundColor Magenta
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./services_data
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./media
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue ./static
Write-Host "Related files removed successfully!" -ForegroundColor Green