chore: update templates (backend v0.4.0 → v0.6.0, caddy v0.1.2 → v0.1.4, frontend v0.4.2 → v0.6.0, infra v0.5.0 → v0.6.0) #14
No files matched your search
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
_commit: v0.4.0
|
_commit: v0.6.0
|
||||||
_src_path: https://git.kotikot.com/templates/backend-python.git
|
_src_path: https://git.kotikot.com/templates/backend-python.git
|
||||||
author_email: h@kotikot.com
|
author_email: h@kotikot.com
|
||||||
author_name: h
|
author_name: h
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ TZ=UTC
|
|||||||
LOG__LEVEL=INFO
|
LOG__LEVEL=INFO
|
||||||
LOG__LEVEL_EXTERNAL=WARNING
|
LOG__LEVEL_EXTERNAL=WARNING
|
||||||
LOG__SHOW_TIME=false
|
LOG__SHOW_TIME=false
|
||||||
|
LOG__SHOW_LOCALS=false
|
||||||
LOG__CONSOLE_WIDTH=150
|
LOG__CONSOLE_WIDTH=150
|
||||||
|
|
||||||
DB__HOST=postgres
|
DB__HOST=postgres
|
||||||
|
|||||||
+4
-3
@@ -1,7 +1,8 @@
|
|||||||
## Checking commands
|
## Checking commands
|
||||||
After writing code, always run from `backend/`:
|
After writing code, always run from `backend/`:
|
||||||
```shell
|
```shell
|
||||||
ruff format
|
make fmt
|
||||||
ruff check --fix
|
make check
|
||||||
ty check
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Mind startup imports: `make check` runs `scripts/check_imports.py`.
|
||||||
+5
-15
@@ -1,12 +1,15 @@
|
|||||||
COMPOSE := cd .. && docker compose
|
COMPOSE := cd .. && docker compose
|
||||||
|
<<<<<<< before updating
|
||||||
IMAGE := beavergram/backend
|
IMAGE := beavergram/backend
|
||||||
IMAGE_INPUTS := pyproject.toml uv.lock Dockerfile
|
IMAGE_INPUTS := pyproject.toml uv.lock Dockerfile
|
||||||
CODE := src scripts ../frontend
|
CODE := src scripts ../frontend
|
||||||
SERVICES := api userbot watcher
|
SERVICES := api userbot watcher
|
||||||
changed = ! git diff --quiet $(1) -- $(2) 2>/dev/null
|
changed = ! git diff --quiet $(1) -- $(2) 2>/dev/null
|
||||||
stamp = { git update-ref $(1) HEAD 2>/dev/null || true; }
|
stamp = { git update-ref $(1) HEAD 2>/dev/null || true; }
|
||||||
|
=======
|
||||||
|
>>>>>>> after updating
|
||||||
|
|
||||||
.PHONY: fmt check build pre-deploy post-deploy migrate revision session
|
.PHONY: fmt check build migrate revision session
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
uv run ruff format
|
uv run ruff format
|
||||||
@@ -16,24 +19,11 @@ check:
|
|||||||
uv run ruff format --check
|
uv run ruff format --check
|
||||||
uv run ruff check
|
uv run ruff check
|
||||||
uv run ty check
|
uv run ty check
|
||||||
|
uv run python scripts/check_imports.py
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(COMPOSE) build api
|
$(COMPOSE) build api
|
||||||
|
|
||||||
pre-deploy:
|
|
||||||
@if $(call changed,refs/deploy/backend,$(IMAGE_INPUTS)) || ! docker image inspect $(IMAGE) >/dev/null 2>&1; then \
|
|
||||||
$(MAKE) build && $(call stamp,refs/deploy/backend) && $(call stamp,refs/deploy/backend-code); \
|
|
||||||
fi
|
|
||||||
@$(COMPOSE) config --services | grep -qx postgres && $(COMPOSE) up -d postgres || true
|
|
||||||
$(MAKE) migrate
|
|
||||||
|
|
||||||
post-deploy:
|
|
||||||
@if $(call changed,refs/deploy/backend-code,$(CODE)); then \
|
|
||||||
running=$$($(COMPOSE) ps --services --status running | grep -x $(addprefix -e ,$(SERVICES))); \
|
|
||||||
[ -z "$$running" ] || $(COMPOSE) restart $$running; \
|
|
||||||
$(call stamp,refs/deploy/backend-code); \
|
|
||||||
fi
|
|
||||||
|
|
||||||
migrate:
|
migrate:
|
||||||
$(COMPOSE) run --rm migrator $(or $(filter-out $@,$(MAKECMDGOALS)),upgrade head)
|
$(COMPOSE) run --rm migrator $(or $(filter-out $@,$(MAKECMDGOALS)),upgrade head)
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,20 @@ if config.config_file_name is not None:
|
|||||||
target_metadata = SQLModel.metadata
|
target_metadata = SQLModel.metadata
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< before updating
|
||||||
def _async_url() -> str:
|
def _async_url() -> str:
|
||||||
return env.db.connection_url.replace("postgresql://", "postgresql+asyncpg://", 1)
|
return env.db.connection_url.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||||
|
=======
|
||||||
|
def include_name(name: str | None, type_: str, _parents: object) -> bool:
|
||||||
|
return not (type_ == "table" and name is not None and name.startswith("taskiq_"))
|
||||||
|
>>>>>>> after updating
|
||||||
|
|
||||||
|
|
||||||
def run_migrations_offline() -> None:
|
def run_migrations_offline() -> None:
|
||||||
context.configure(
|
context.configure(
|
||||||
url=_async_url(),
|
url=_async_url(),
|
||||||
target_metadata=target_metadata,
|
target_metadata=target_metadata,
|
||||||
|
include_name=include_name,
|
||||||
literal_binds=True,
|
literal_binds=True,
|
||||||
dialect_opts={"paramstyle": "named"},
|
dialect_opts={"paramstyle": "named"},
|
||||||
)
|
)
|
||||||
@@ -31,8 +37,17 @@ def run_migrations_offline() -> None:
|
|||||||
context.run_migrations()
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< before updating
|
||||||
def _do_run_migrations(connection) -> None: # noqa: ANN001
|
def _do_run_migrations(connection) -> None: # noqa: ANN001
|
||||||
context.configure(connection=connection, target_metadata=target_metadata)
|
context.configure(connection=connection, target_metadata=target_metadata)
|
||||||
|
=======
|
||||||
|
def do_run_migrations(connection: Connection) -> None:
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
include_name=include_name,
|
||||||
|
)
|
||||||
|
>>>>>>> after updating
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
context.run_migrations()
|
context.run_migrations()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HEAVY = ["aiogram", "pyrogram", "fastapi", "google.genai"]
|
||||||
|
SERVICES = {
|
||||||
|
"api": (["api.app"], {"fastapi"}),
|
||||||
|
"userbot": (["userbot.runner", "userbot.handlers"], {"pyrogram"}),
|
||||||
|
}
|
||||||
|
PROBE = """
|
||||||
|
import importlib, sys
|
||||||
|
for module in sys.argv[1].split(","):
|
||||||
|
importlib.import_module(module)
|
||||||
|
print(" ".join(name for name in sys.argv[2:] if name in sys.modules))
|
||||||
|
"""
|
||||||
|
ENV = dict(os.environ)
|
||||||
|
|
||||||
|
|
||||||
|
def loaded(modules: list[str]) -> tuple[set[str], str]:
|
||||||
|
result = subprocess.run( # noqa: S603
|
||||||
|
[sys.executable, "-c", PROBE, ",".join(modules), *HEAVY],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd="src",
|
||||||
|
env=ENV,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
error = (result.stderr.strip().splitlines() or ["exit code"])[-1]
|
||||||
|
return set(result.stdout.split()) & set(HEAVY), error if result.returncode else ""
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
failed = False
|
||||||
|
for service, (modules, allowed) in SERVICES.items():
|
||||||
|
found, error = loaded(modules)
|
||||||
|
extra = sorted(found - allowed)
|
||||||
|
status = error or ("imports " + ", ".join(extra) if extra else "")
|
||||||
|
print(f"{service:10s} {'FAIL: ' + status if status else 'ok'}")
|
||||||
|
failed |= bool(status)
|
||||||
|
return int(failed)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -5,6 +5,7 @@ from userbot.modules.client import PyroClient
|
|||||||
from utils.env import env
|
from utils.env import env
|
||||||
from utils.logging import logger, setup_logging
|
from utils.logging import logger, setup_logging
|
||||||
|
|
||||||
|
<<<<<<< before updating
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ def _finalize_session(sessions_dir: Path, name: str) -> Path:
|
|||||||
target = sessions_dir / f"{name}.session"
|
target = sessions_dir / f"{name}.session"
|
||||||
(sessions_dir / "generated.session").rename(target)
|
(sessions_dir / "generated.session").rename(target)
|
||||||
return target
|
return target
|
||||||
|
=======
|
||||||
|
from userbot.modules.client import PyroClient
|
||||||
|
>>>>>>> after updating
|
||||||
|
|
||||||
|
|
||||||
async def main() -> None:
|
async def main() -> None:
|
||||||
|
|||||||
@@ -3,4 +3,8 @@ from dishka import make_async_container
|
|||||||
from dependencies.providers.postgres import DbProvider
|
from dependencies.providers.postgres import DbProvider
|
||||||
from dependencies.providers.storage import StorageProvider
|
from dependencies.providers.storage import StorageProvider
|
||||||
|
|
||||||
|
<<<<<<< before updating
|
||||||
container = make_async_container(DbProvider(), StorageProvider())
|
container = make_async_container(DbProvider(), StorageProvider())
|
||||||
|
=======
|
||||||
|
container = make_async_container(DbProvider(), RepositoryProvider())
|
||||||
|
>>>>>>> after updating
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
<<<<<<< before updating
|
||||||
from userbot.modules.client import DEVICE_MODEL, DEVICE_MODEL_LIMIT, PyroClient
|
from userbot.modules.client import DEVICE_MODEL, DEVICE_MODEL_LIMIT, PyroClient
|
||||||
|
|
||||||
__all__ = ["DEVICE_MODEL", "DEVICE_MODEL_LIMIT", "PyroClient"]
|
__all__ = ["DEVICE_MODEL", "DEVICE_MODEL_LIMIT", "PyroClient"]
|
||||||
|
=======
|
||||||
|
>>>>>>> after updating
|
||||||
@@ -4,7 +4,27 @@ from pydantic import Field, SecretStr
|
|||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
<<<<<<< before updating
|
||||||
class DatabaseSettings(BaseSettings):
|
class DatabaseSettings(BaseSettings):
|
||||||
|
=======
|
||||||
|
def is_prod() -> bool:
|
||||||
|
return os.getenv("RUN_ENVIRONMENT") == "prod"
|
||||||
|
|
||||||
|
|
||||||
|
class Section(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(extra="ignore")
|
||||||
|
|
||||||
|
|
||||||
|
class LogSettings(Section):
|
||||||
|
level: str = "INFO"
|
||||||
|
level_external: str = "WARNING"
|
||||||
|
show_time: bool = False
|
||||||
|
show_locals: bool = False
|
||||||
|
console_width: int = 150
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseSettings(Section):
|
||||||
|
>>>>>>> after updating
|
||||||
host: str = "postgres"
|
host: str = "postgres"
|
||||||
port: int = 5432
|
port: int = 5432
|
||||||
user: str = "beavergram"
|
user: str = "beavergram"
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import asyncio
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
|
||||||
|
async def preload(module: str) -> None:
|
||||||
|
await asyncio.to_thread(import_module, module)
|
||||||
@@ -20,13 +20,13 @@ def setup_logging() -> None:
|
|||||||
markup=True,
|
markup=True,
|
||||||
rich_tracebacks=True,
|
rich_tracebacks=True,
|
||||||
enable_link_path=False,
|
enable_link_path=False,
|
||||||
tracebacks_show_locals=True,
|
tracebacks_show_locals=env.log.show_locals,
|
||||||
omit_repeated_times=False,
|
omit_repeated_times=False,
|
||||||
show_time=env.log.show_time,
|
show_time=env.log.show_time,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
install(console=console, show_locals=True)
|
install(console=console, show_locals=env.log.show_locals)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("beavergram")
|
logger = logging.getLogger("beavergram")
|
||||||
|
|||||||
Reference in new issue
Block a user