refactor: enhance infrastructure stability and fix circular imports

- update postgres and valkey healthchecks to include default values and password support
- switch valkey startup command to shell script for conditional authentication
- change production database port mapping from 6767 to 5001
- resolve circular imports in user events by localizing keyboard imports
- refactor LocaleList to robustly validate both native lists and csv strings
This commit is contained in:
Ilay
2026-03-24 23:51:39 +05:00
parent fe9f562c3e
commit 72dbc3f850
6 changed files with 57 additions and 26 deletions

View File

@@ -50,7 +50,7 @@ services:
- remnashop-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
test: ["CMD-SHELL", "pg_isready -U $${DATABASE_USER:-remnashop} -d $${DATABASE_NAME:-remnashop}"]
interval: 3s
timeout: 10s
retries: 3
@@ -63,17 +63,19 @@ services:
<<: [*common, *logging, *env]
command: >
valkey-server
--save ""
--appendonly no
--maxmemory-policy noeviction
--loglevel warning
sh -c '
ARGS="--save \"\" --appendonly no --maxmemory-policy noeviction --loglevel warning";
if [ -n "$${REDIS_PASSWORD}" ]; then
exec valkey-server $${ARGS} --requirepass "$${REDIS_PASSWORD}";
else
exec valkey-server $${ARGS};
fi'
volumes:
- remnashop-redis-data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
test: ["CMD-SHELL", "valkey-cli $${REDIS_PASSWORD:+-a \"$REDIS_PASSWORD\"} ping | grep PONG"]
interval: 3s
timeout: 10s
retries: 3

View File

@@ -35,19 +35,19 @@ services:
<<: [*common, *logging, *env]
environment:
- POSTGRES_USER=${DATABASE_USER}
- POSTGRES_USER=${DATABASE_USER:-remnashop}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}
- POSTGRES_DB=${DATABASE_NAME:-remnashop}
- TZ=UTC
ports:
- 127.0.0.1:6767:${DATABASE_PORT:-5432}
- 127.0.0.1:5001:${DATABASE_PORT:-5432}
volumes:
- remnashop-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
test: ["CMD-SHELL", "pg_isready -U $${DATABASE_USER:-remnashop} -d $${DATABASE_NAME:-remnashop}"]
interval: 3s
timeout: 10s
retries: 3
@@ -60,13 +60,19 @@ services:
<<: [*common, *logging, *env]
command: >
--requirepass ${REDIS_PASSWORD}
sh -c '
ARGS="--save \"\" --appendonly no --maxmemory-policy noeviction --loglevel warning";
if [ -n "$${REDIS_PASSWORD}" ]; then
exec valkey-server $${ARGS} --requirepass "$${REDIS_PASSWORD}";
else
exec valkey-server $${ARGS};
fi'
volumes:
- remnashop-redis-data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
test: ["CMD-SHELL", "valkey-cli $${REDIS_PASSWORD:+-a \"$REDIS_PASSWORD\"} ping | grep PONG"]
interval: 3s
timeout: 10s
retries: 3

View File

@@ -35,19 +35,19 @@ services:
<<: [*common, *logging, *env]
environment:
- POSTGRES_USER=${DATABASE_USER}
- POSTGRES_USER=${DATABASE_USER:-remnashop}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}
- POSTGRES_DB=${DATABASE_NAME:-remnashop}
- TZ=UTC
ports:
- 127.0.0.1:6767:${DATABASE_PORT:-5432}
- 127.0.0.1:5001:${DATABASE_PORT:-5432}
volumes:
- remnashop-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
test: ["CMD-SHELL", "pg_isready -U $${DATABASE_USER:-remnashop} -d $${DATABASE_NAME:-remnashop}"]
interval: 3s
timeout: 10s
retries: 3
@@ -60,13 +60,19 @@ services:
<<: [*common, *logging, *env]
command: >
--requirepass ${REDIS_PASSWORD}
sh -c '
ARGS="--save \"\" --appendonly no --maxmemory-policy noeviction --loglevel warning";
if [ -n "$${REDIS_PASSWORD}" ]; then
exec valkey-server $${ARGS} --requirepass "$${REDIS_PASSWORD}";
else
exec valkey-server $${ARGS};
fi'
volumes:
- remnashop-redis-data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
test: ["CMD-SHELL", "valkey-cli $${REDIS_PASSWORD:+-a \"$REDIS_PASSWORD\"} ping | grep PONG"]
interval: 3s
timeout: 10s
retries: 3

View File

@@ -6,7 +6,6 @@ from remnapy.enums.users import TrafficLimitStrategy
from src.application.dto.message_payload import MessagePayloadDto
from src.core.enums import MessageEffectId, ReferralRewardType, UserNotificationType
from src.core.types import NotificationType
from src.telegram.keyboards import get_buy_keyboard, get_renew_keyboard
from .base import UserEvent
@@ -27,6 +26,8 @@ class SubscriptionLimitedEvent(UserEvent):
return "event-subscription.limited"
def as_payload(self) -> "MessagePayloadDto":
from src.telegram.keyboards import get_buy_keyboard, get_renew_keyboard # noqa: PLC0415
keyboard = get_buy_keyboard() if self.is_trial else get_renew_keyboard()
return MessagePayloadDto(
@@ -52,6 +53,8 @@ class SubscriptionExpiredEvent(UserEvent):
return "event-subscription.expired"
def as_payload(self) -> "MessagePayloadDto":
from src.telegram.keyboards import get_buy_keyboard, get_renew_keyboard # noqa: PLC0415
keyboard = get_buy_keyboard() if self.is_trial else get_renew_keyboard()
return MessagePayloadDto(
@@ -78,6 +81,8 @@ class SubscriptionExpiresEvent(UserEvent):
return "event-subscription.expiring"
def as_payload(self) -> "MessagePayloadDto":
from src.telegram.keyboards import get_buy_keyboard, get_renew_keyboard # noqa: PLC0415
keyboard = get_buy_keyboard() if self.is_trial else get_renew_keyboard()
return MessagePayloadDto(

View File

@@ -24,7 +24,7 @@ class AppConfig(BaseConfig, env_prefix="APP_"):
host: str = "0.0.0.0"
port: int = 5000
locales: LocaleList = LocaleList([Locale.RU]) # TODO: Change to EN
locales: LocaleList = [Locale.RU] # TODO: Change to EN
default_locale: Locale = Locale.RU # TODO: Change to EN
crypt_key: SecretStr

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Annotated, NewType, TypeAlias, Union
from typing import TYPE_CHECKING, Annotated, Any, List, NewType, TypeAlias, Union
from aiogram.types import (
ForceReply,
@@ -34,9 +34,21 @@ RemnaUserDto: TypeAlias = Union[UserWebhookDto, UserResponseDto]
StringList: TypeAlias = Annotated[
ListStr, PlainValidator(lambda x: [s.strip() for s in x.split(",")])
]
def _validate_locale_list(x: Any) -> List[Locale]:
if isinstance(x, list):
return [v if isinstance(v, Locale) else Locale(str(v).strip()) for v in x]
if isinstance(x, str):
if not x.strip():
return []
return [Locale(loc.strip()) for loc in x.split(",")]
raise ValueError(f"Expected list or str, got {type(x)}")
LocaleList: TypeAlias = Annotated[
ListLocale,
PlainValidator(
func=lambda x: [Locale(loc.strip()) for loc in (x if isinstance(x, list) else x.split(","))]
),
List[Locale],
PlainValidator(_validate_locale_list),
]