From aac037676c7673a1fc9af272218e31349c4d30e7 Mon Sep 17 00:00:00 2001 From: h Date: Tue, 8 Sep 2026 20:38:27 +0200 Subject: [PATCH] Single part at the root, COMPOSE_FILE in .env --- README.md | 4 +-- copier.yml | 24 +++++++++++++-- template/ty.toml | 5 ---- template/ty.toml.jinja | 5 ++++ .../{backend => {{ part_dir }}}/.dockerignore | 0 .../.env.example.jinja | 3 +- .../.python-version | 0 .../Dockerfile.jinja | 0 .../Makefile.jinja | 30 ++++++++++++++++--- .../docker-compose.local.yml.jinja | 0 .../docker-compose.prod.yml.jinja | 0 .../docker-compose.yml.jinja | 24 +++++++-------- .../pyproject.toml.jinja | 0 .../create.py.jinja | 0 .../edit.py.jinja | 0 .../src/utils/__init__.py.jinja | 0 .../src/utils/env.py.jinja | 0 .../src/utils/logging.py.jinja | 0 .../__init__.py.jinja | 0 .../models/__init__.py.jinja | 0 .../models/user.py.jinja | 0 ...e == 'postgres' %}base.py{% endif %}.jinja | 0 ...ynamic_config %}config.py{% endif %}.jinja | 0 .../__init__.py.jinja | 0 .../user.py.jinja | 0 ...ynamic_config %}config.py{% endif %}.jinja | 0 ...% if use_redis %}cache.py{% endif %}.jinja | 0 ...ynamic_config %}config.py{% endif %}.jinja | 0 .../__init__.py.jinja | 0 .../__main__.py.jinja | 0 .../common.py.jinja | 0 .../filters/__init__.py.jinja | 0 .../filters/admin.py.jinja | 0 .../handlers/__init__.py.jinja | 0 .../handlers/initialize/__init__.py.jinja | 0 .../handlers/initialize/initializer.py.jinja | 0 .../handlers/start/__init__.py.jinja | 0 .../handlers/start/start.py.jinja | 0 .../__init__.py.jinja | 0 .../deposit.py.jinja | 0 .../__init__.py.jinja | 0 .../withdraw.py.jinja | 0 .../keyboards/__init__.py.jinja | 0 .../__init__.py.jinja | 0 .../__main__.py.jinja | 0 .../app.py.jinja | 0 .../middlewares/__init__.py.jinja | 0 .../modules/__init__.py.jinja | 0 .../routers/__init__.py.jinja | 0 .../routers/api/__init__.py.jinja | 0 .../routers/api/health.py.jinja | 0 .../schemas/__init__.py.jinja | 0 .../__init__.py.jinja | 0 .../__main__.py.jinja | 0 .../handlers/__init__.py.jinja | 0 .../handlers/echo.py.jinja | 0 .../handlers/private/__init__.py.jinja | 0 .../handlers/private/pm_message.py.jinja | 0 .../modules/__init__.py | 0 .../modules/client.py.jinja | 0 .../runner.py.jinja | 0 .../__init__.py.jinja | 0 .../__main__.py.jinja | 0 .../__init__.py.jinja | 0 .../__main__.py.jinja | 0 .../broker.py.jinja | 0 .../tasks/__init__.py.jinja | 0 .../tasks/example.py.jinja | 0 .../__init__.py | 0 .../container.py.jinja | 0 .../integrations/__init__.py.jinja | 0 .../integrations/asyncio.py.jinja | 0 .../providers/__init__.py | 0 ...atabase != 'none' %}db.py{% endif %}.jinja | 0 ...stgres' %}repositories.py{% endif %}.jinja | 0 ...ynamic_config %}config.py{% endif %}.jinja | 0 .../{% if use_llm %}llm.py{% endif %}.jinja | 0 ...% if use_redis %}redis.py{% endif %}.jinja | 0 .../types/__init__.py.jinja | 0 .../.gitkeep | 0 ... 'postgres' %}alembic.ini{% endif %}.jinja | 0 .../env.py | 0 .../script.py.mako | 0 .../versions/.gitkeep | 0 ... layout == 'part' %}.gitignore{% endif %}} | 0 ...f layout == 'part' %}CLAUDE.md{% endif %}} | 0 86 files changed, 68 insertions(+), 27 deletions(-) delete mode 100644 template/ty.toml create mode 100644 template/ty.toml.jinja rename template/{backend => {{ part_dir }}}/.dockerignore (100%) rename template/{backend => {{ part_dir }}}/.env.example.jinja (87%) rename template/{backend => {{ part_dir }}}/.python-version (100%) rename template/{backend => {{ part_dir }}}/Dockerfile.jinja (100%) rename template/{backend => {{ part_dir }}}/Makefile.jinja (60%) rename template/{backend => {{ part_dir }}}/docker-compose.local.yml.jinja (100%) rename template/{backend => {{ part_dir }}}/docker-compose.prod.yml.jinja (100%) rename template/{backend => {{ part_dir }}}/docker-compose.yml.jinja (87%) rename template/{backend => {{ part_dir }}}/pyproject.toml.jinja (100%) rename template/{backend => {{ part_dir }}}/scripts/{% if 'kurigram_userbot' in backend_services %}session{% endif %}/create.py.jinja (100%) rename template/{backend => {{ part_dir }}}/scripts/{% if dynamic_config %}config{% endif %}/edit.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/env.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/logging.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/models/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/models/user.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if database == 'postgres' %}base.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if dynamic_config %}config.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/user.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if dynamic_config %}config.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if use_redis %}cache.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/utils/{% if dynamic_config %}config.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__main__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/common.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/admin.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/initializer.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/start.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/deposit.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/withdraw.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/keyboards/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/__main__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/app.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/middlewares/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/modules/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/routers/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/health.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'api' in backend_services %}api{% endif %}/schemas/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__main__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/echo.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/pm_message.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/__init__.py (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/client.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/runner.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__main__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__main__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/broker.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/example.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/__init__.py (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/container.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/integrations/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/integrations/asyncio.py.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/__init__.py (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database != 'none' %}db.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database == 'postgres' %}repositories.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if dynamic_config %}config.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_llm %}llm.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_redis %}redis.py{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/src/{% if use_dishka %}dependencies{% endif %}/types/__init__.py.jinja (100%) rename template/{backend => {{ part_dir }}}/{% if 'kurigram_userbot' in backend_services %}sessions{% endif %}/.gitkeep (100%) rename template/{backend => {{ part_dir }}}/{% if database == 'postgres' %}alembic.ini{% endif %}.jinja (100%) rename template/{backend => {{ part_dir }}}/{% if database == 'postgres' %}migrations{% endif %}/env.py (100%) rename template/{backend => {{ part_dir }}}/{% if database == 'postgres' %}migrations{% endif %}/script.py.mako (100%) rename template/{backend => {{ part_dir }}}/{% if database == 'postgres' %}migrations{% endif %}/versions/.gitkeep (100%) rename template/{backend/.gitignore => {{ part_dir }}/{% if layout == 'part' %}.gitignore{% endif %}} (100%) rename template/{backend/CLAUDE.md => {{ part_dir }}/{% if layout == 'part' %}CLAUDE.md{% endif %}} (100%) diff --git a/README.md b/README.md index 39489c4..034a008 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # backend-python -Copier template for a `backend/` part: a uv project with one Docker image and one `python -m ` entrypoint per service (`api`, `bot`, `userbot`, `worker`, `scheduler`), pydantic-settings, rich logging, optional Dishka, MongoDB or PostgreSQL, Redis, taskiq, CryptoPay, pydantic-ai and a DynamicConfig stored in the database. +Copier template for a Python backend, either as a `backend/` part next to other parts or alone at the root of the project (`layout`): a uv project with one Docker image and one `python -m ` entrypoint per service (`api`, `bot`, `userbot`, `worker`, `scheduler`), pydantic-settings, rich logging, optional Dishka, MongoDB or PostgreSQL, Redis, taskiq, CryptoPay, pydantic-ai and a DynamicConfig stored in the database. -Ships `backend/docker-compose.yml` with root-relative paths, `docker-compose.local.yml` (published ports) and `docker-compose.prod.yml` (the `caddy` network), a `Makefile` with `fmt`, `check`, `build` and service-specific targets, `.env.example` and `CLAUDE.md`. Meant to be applied on top of the `infra` template. +Ships `backend/docker-compose.yml` with root-relative paths, `docker-compose.local.yml` (published ports) and `docker-compose.prod.yml` (the `caddy` network), a `Makefile` with `fmt`, `check`, `build` and service-specific targets, `.env.example` and `CLAUDE.md`. Applied on top of the `infra` template, `layout: parts` or `layout: single` respectively. ```sh copier copy --trust -d project_name="My Thing" diff --git a/copier.yml b/copier.yml index c10bff6..67d43ab 100644 --- a/copier.yml +++ b/copier.yml @@ -25,6 +25,14 @@ author_email: type: str default: "" +layout: + type: str + help: Where the backend lives + choices: + In backend/ next to other parts: part + At the root of the project: root + default: part + backend_services: type: str multiselect: true @@ -84,7 +92,7 @@ dynamic_config: serve_spa: type: bool - when: "{{ 'api' in backend_services }}" + when: "{{ layout == 'part' and 'api' in backend_services }}" help: Serve the frontend build from the api as a SPA default: false @@ -93,6 +101,16 @@ install_deps: help: uv sync after generation default: true +part_dir: + type: str + when: false + default: "{{ 'backend' if layout == 'part' else '.' }}" + +prefix: + type: str + when: false + default: "{{ 'backend/' if layout == 'part' else '' }}" + _tasks: - - "{% if _copier_operation == 'copy' and install_deps %}sh -c 'cd backend && uv sync'{% else %}true{% endif %}" - - "{% if install_deps %}sh -c 'cd backend && uv run ruff format -q'{% else %}true{% endif %}" + - "{% if _copier_operation == 'copy' and install_deps %}sh -c 'cd {{ part_dir }} && uv sync'{% else %}true{% endif %}" + - "{% if install_deps %}sh -c 'cd {{ part_dir }} && uv run ruff format -q'{% else %}true{% endif %}" diff --git a/template/ty.toml b/template/ty.toml deleted file mode 100644 index d9a9e6a..0000000 --- a/template/ty.toml +++ /dev/null @@ -1,5 +0,0 @@ -[environment] -python = "backend/.venv" - -[src] -exclude = ["backend/migrations"] diff --git a/template/ty.toml.jinja b/template/ty.toml.jinja new file mode 100644 index 0000000..8190011 --- /dev/null +++ b/template/ty.toml.jinja @@ -0,0 +1,5 @@ +[environment] +python = "{{ prefix }}.venv" + +[src] +exclude = ["{{ prefix }}migrations"] diff --git a/template/backend/.dockerignore b/template/{{ part_dir }}/.dockerignore similarity index 100% rename from template/backend/.dockerignore rename to template/{{ part_dir }}/.dockerignore diff --git a/template/backend/.env.example.jinja b/template/{{ part_dir }}/.env.example.jinja similarity index 87% rename from template/backend/.env.example.jinja rename to template/{{ part_dir }}/.env.example.jinja index ffb8c88..f50d150 100644 --- a/template/backend/.env.example.jinja +++ b/template/{{ part_dir }}/.env.example.jinja @@ -1,4 +1,5 @@ -RUN_ENVIRONMENT=dev +{% if layout == 'root' %}COMPOSE_FILE=docker-compose.yml:docker-compose.local.yml +{% endif %}RUN_ENVIRONMENT=dev LOG__LEVEL=INFO LOG__LEVEL_EXTERNAL=WARNING LOG__SHOW_TIME=false diff --git a/template/backend/.python-version b/template/{{ part_dir }}/.python-version similarity index 100% rename from template/backend/.python-version rename to template/{{ part_dir }}/.python-version diff --git a/template/backend/Dockerfile.jinja b/template/{{ part_dir }}/Dockerfile.jinja similarity index 100% rename from template/backend/Dockerfile.jinja rename to template/{{ part_dir }}/Dockerfile.jinja diff --git a/template/backend/Makefile.jinja b/template/{{ part_dir }}/Makefile.jinja similarity index 60% rename from template/backend/Makefile.jinja rename to template/{{ part_dir }}/Makefile.jinja index bc818dd..2f349bc 100644 --- a/template/backend/Makefile.jinja +++ b/template/{{ part_dir }}/Makefile.jinja @@ -5,9 +5,11 @@ {%- if 'kurigram_userbot' in backend_services %}{% set _ = svc_modules.append("userbot") %}{% endif -%} {%- if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}{% set _ = svc_modules.append("scheduler") %}{% endif -%} {%- set has_script = dynamic_config and svc_modules -%} -COMPOSE := docker compose --project-directory .. - -.PHONY: fmt check build{% if database == 'postgres' %} migrate revision{% endif %}{% if has_script %} script{% endif %}{% if 'kurigram_userbot' in backend_services %} session{% endif %} +COMPOSE := docker compose{% if layout == 'part' %} --project-directory ..{% endif %} +{% if layout == 'root' %}SERVICES := $(COMPOSE) --profile services +EXTERNAL := $(COMPOSE) --profile external +{% endif %} +.PHONY: {% if layout == 'root' %}env external recreate rebuild restart logs down {% endif %}fmt check build{% if database == 'postgres' %} migrate revision{% endif %}{% if has_script %} script{% endif %}{% if 'kurigram_userbot' in backend_services %} session{% endif %} fmt: uv run ruff format @@ -20,7 +22,27 @@ check: build: $(COMPOSE) build {{ svc_modules[0] }} -{% if database == 'postgres' %} +{% if layout == 'root' %} +env: + @test -f .env || cp .env.example .env + +external: + $(EXTERNAL) up -d + +recreate: external + $(SERVICES) up -d --force-recreate + +rebuild: build recreate + +restart: + $(SERVICES) restart + +logs: + $(SERVICES) logs -f --tail=100 + +down: + $(EXTERNAL) --profile services down +{% endif %}{% if database == 'postgres' %} migrate: $(COMPOSE) --profile external run --rm migrator diff --git a/template/backend/docker-compose.local.yml.jinja b/template/{{ part_dir }}/docker-compose.local.yml.jinja similarity index 100% rename from template/backend/docker-compose.local.yml.jinja rename to template/{{ part_dir }}/docker-compose.local.yml.jinja diff --git a/template/backend/docker-compose.prod.yml.jinja b/template/{{ part_dir }}/docker-compose.prod.yml.jinja similarity index 100% rename from template/backend/docker-compose.prod.yml.jinja rename to template/{{ part_dir }}/docker-compose.prod.yml.jinja diff --git a/template/backend/docker-compose.yml.jinja b/template/{{ part_dir }}/docker-compose.yml.jinja similarity index 87% rename from template/backend/docker-compose.yml.jinja rename to template/{{ part_dir }}/docker-compose.yml.jinja index b685a73..640a2b4 100644 --- a/template/backend/docker-compose.yml.jinja +++ b/template/{{ part_dir }}/docker-compose.yml.jinja @@ -9,21 +9,21 @@ {%- if 'kurigram_userbot' in backend_services %}{% set _ = app_services.append({'name': 'userbot', 'command': '[userbot]'}) %}{% endif -%} {%- if 'scheduler' in backend_services %}{% set _ = app_services.append({'name': 'scheduler', 'command': ('[worker, scheduler]' if 'taskiq_worker' in backend_services else '[scheduler]')}) %}{% endif -%} {%- macro backend_service(name, command) %} {{ name }}: - build: backend + build: {{ part_dir }} image: {{ project_slug }}/backend profiles: [{{ name }}, services] restart: unless-stopped env_file: - path: .env required: false - - path: backend/.env +{% if layout == 'part' %} - path: backend/.env required: false - environment: +{% endif %} environment: RUN_ENVIRONMENT: prod volumes: - - ./backend/src:/app/src -{% if has_scripts %} - ./backend/scripts:/app/scripts -{% endif %}{% if name == 'userbot' %} - ./backend/sessions:/app/sessions + - ./{{ prefix }}src:/app/src +{% if has_scripts %} - ./{{ prefix }}scripts:/app/scripts +{% endif %}{% if name == 'userbot' %} - ./{{ prefix }}sessions:/app/sessions {% endif %}{% if name == 'api' and serve_spa %} - ./frontend/build:/app/static:ro {% endif %} command: {{ command }} {% if svc_networks %} networks: @@ -70,20 +70,20 @@ services: - ${DB__HOST:-postgres} migrator: - build: backend + build: {{ part_dir }} image: {{ project_slug }}/backend profiles: [migrate] env_file: - path: .env required: false - - path: backend/.env +{% if layout == 'part' %} - path: backend/.env required: false - environment: +{% endif %} environment: RUN_ENVIRONMENT: prod volumes: - - ./backend/src:/app/src - - ./backend/migrations:/app/migrations - - ./backend/alembic.ini:/app/alembic.ini + - ./{{ prefix }}src:/app/src + - ./{{ prefix }}migrations:/app/migrations + - ./{{ prefix }}alembic.ini:/app/alembic.ini entrypoint: [alembic] command: [upgrade, head] depends_on: diff --git a/template/backend/pyproject.toml.jinja b/template/{{ part_dir }}/pyproject.toml.jinja similarity index 100% rename from template/backend/pyproject.toml.jinja rename to template/{{ part_dir }}/pyproject.toml.jinja diff --git a/template/backend/scripts/{% if 'kurigram_userbot' in backend_services %}session{% endif %}/create.py.jinja b/template/{{ part_dir }}/scripts/{% if 'kurigram_userbot' in backend_services %}session{% endif %}/create.py.jinja similarity index 100% rename from template/backend/scripts/{% if 'kurigram_userbot' in backend_services %}session{% endif %}/create.py.jinja rename to template/{{ part_dir }}/scripts/{% if 'kurigram_userbot' in backend_services %}session{% endif %}/create.py.jinja diff --git a/template/backend/scripts/{% if dynamic_config %}config{% endif %}/edit.py.jinja b/template/{{ part_dir }}/scripts/{% if dynamic_config %}config{% endif %}/edit.py.jinja similarity index 100% rename from template/backend/scripts/{% if dynamic_config %}config{% endif %}/edit.py.jinja rename to template/{{ part_dir }}/scripts/{% if dynamic_config %}config{% endif %}/edit.py.jinja diff --git a/template/backend/src/utils/__init__.py.jinja b/template/{{ part_dir }}/src/utils/__init__.py.jinja similarity index 100% rename from template/backend/src/utils/__init__.py.jinja rename to template/{{ part_dir }}/src/utils/__init__.py.jinja diff --git a/template/backend/src/utils/env.py.jinja b/template/{{ part_dir }}/src/utils/env.py.jinja similarity index 100% rename from template/backend/src/utils/env.py.jinja rename to template/{{ part_dir }}/src/utils/env.py.jinja diff --git a/template/backend/src/utils/logging.py.jinja b/template/{{ part_dir }}/src/utils/logging.py.jinja similarity index 100% rename from template/backend/src/utils/logging.py.jinja rename to template/{{ part_dir }}/src/utils/logging.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/__init__.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/__init__.py.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/__init__.py.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/__init__.py.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/__init__.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/user.py.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/user.py.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/user.py.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/user.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if database == 'postgres' %}base.py{% endif %}.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if database == 'postgres' %}base.py{% endif %}.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if database == 'postgres' %}base.py{% endif %}.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if database == 'postgres' %}base.py{% endif %}.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if dynamic_config %}config.py{% endif %}.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if dynamic_config %}config.py{% endif %}.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if dynamic_config %}config.py{% endif %}.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/models/{% if dynamic_config %}config.py{% endif %}.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/__init__.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/user.py.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/user.py.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/user.py.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/user.py.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if dynamic_config %}config.py{% endif %}.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if dynamic_config %}config.py{% endif %}.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if dynamic_config %}config.py{% endif %}.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if dynamic_config %}config.py{% endif %}.jinja diff --git a/template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if use_redis %}cache.py{% endif %}.jinja b/template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if use_redis %}cache.py{% endif %}.jinja similarity index 100% rename from template/backend/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if use_redis %}cache.py{% endif %}.jinja rename to template/{{ part_dir }}/src/utils/{% if database != 'none' %}db{% endif %}/{% if database == 'postgres' %}repositories{% endif %}/{% if use_redis %}cache.py{% endif %}.jinja diff --git a/template/backend/src/utils/{% if dynamic_config %}config.py{% endif %}.jinja b/template/{{ part_dir }}/src/utils/{% if dynamic_config %}config.py{% endif %}.jinja similarity index 100% rename from template/backend/src/utils/{% if dynamic_config %}config.py{% endif %}.jinja rename to template/{{ part_dir }}/src/utils/{% if dynamic_config %}config.py{% endif %}.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__main__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__main__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__main__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/__main__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/common.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/common.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/common.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/common.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/admin.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/admin.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/admin.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/filters/admin.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/initializer.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/initializer.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/initializer.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/initialize/initializer.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/start.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/start.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/start.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/start/start.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/deposit.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/deposit.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/deposit.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}deposit{% endif %}/deposit.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/withdraw.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/withdraw.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/withdraw.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/handlers/{% if include_payments %}withdraw{% endif %}/withdraw.py.jinja diff --git a/template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/keyboards/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/keyboards/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/keyboards/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'aiogram_bot' in backend_services %}bot{% endif %}/keyboards/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/__main__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/__main__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/__main__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/__main__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/app.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/app.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/app.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/app.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/middlewares/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/middlewares/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/middlewares/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/middlewares/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/modules/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/modules/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/modules/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/modules/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/__init__.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/health.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/health.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/health.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/routers/api/health.py.jinja diff --git a/template/backend/src/{% if 'api' in backend_services %}api{% endif %}/schemas/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/schemas/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'api' in backend_services %}api{% endif %}/schemas/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'api' in backend_services %}api{% endif %}/schemas/__init__.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__main__.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__main__.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__main__.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/__main__.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/__init__.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/echo.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/echo.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/echo.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/echo.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/__init__.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/pm_message.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/pm_message.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/pm_message.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/handlers/private/pm_message.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/__init__.py b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/__init__.py similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/__init__.py rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/__init__.py diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/client.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/client.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/client.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/modules/client.py.jinja diff --git a/template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/runner.py.jinja b/template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/runner.py.jinja similarity index 100% rename from template/backend/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/runner.py.jinja rename to template/{{ part_dir }}/src/{% if 'kurigram_userbot' in backend_services %}userbot{% endif %}/runner.py.jinja diff --git a/template/backend/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__main__.py.jinja b/template/{{ part_dir }}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__main__.py.jinja similarity index 100% rename from template/backend/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__main__.py.jinja rename to template/{{ part_dir }}/src/{% if 'scheduler' in backend_services and 'taskiq_worker' not in backend_services %}scheduler{% endif %}/__main__.py.jinja diff --git a/template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__init__.py.jinja diff --git a/template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__main__.py.jinja b/template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__main__.py.jinja similarity index 100% rename from template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__main__.py.jinja rename to template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/__main__.py.jinja diff --git a/template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/broker.py.jinja b/template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/broker.py.jinja similarity index 100% rename from template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/broker.py.jinja rename to template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/broker.py.jinja diff --git a/template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/__init__.py.jinja b/template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/__init__.py.jinja diff --git a/template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/example.py.jinja b/template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/example.py.jinja similarity index 100% rename from template/backend/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/example.py.jinja rename to template/{{ part_dir }}/src/{% if 'taskiq_worker' in backend_services %}worker{% endif %}/tasks/example.py.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/__init__.py b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/__init__.py similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/__init__.py rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/__init__.py diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/container.py.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/container.py.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/container.py.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/container.py.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/integrations/__init__.py.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/integrations/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/integrations/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/integrations/__init__.py.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/integrations/asyncio.py.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/integrations/asyncio.py.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/integrations/asyncio.py.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/integrations/asyncio.py.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/__init__.py b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/__init__.py similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/__init__.py rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/__init__.py diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database != 'none' %}db.py{% endif %}.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database != 'none' %}db.py{% endif %}.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database != 'none' %}db.py{% endif %}.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database != 'none' %}db.py{% endif %}.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database == 'postgres' %}repositories.py{% endif %}.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database == 'postgres' %}repositories.py{% endif %}.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database == 'postgres' %}repositories.py{% endif %}.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if database == 'postgres' %}repositories.py{% endif %}.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if dynamic_config %}config.py{% endif %}.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if dynamic_config %}config.py{% endif %}.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if dynamic_config %}config.py{% endif %}.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if dynamic_config %}config.py{% endif %}.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_llm %}llm.py{% endif %}.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_llm %}llm.py{% endif %}.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_llm %}llm.py{% endif %}.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_llm %}llm.py{% endif %}.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_redis %}redis.py{% endif %}.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_redis %}redis.py{% endif %}.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_redis %}redis.py{% endif %}.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/providers/{% if use_redis %}redis.py{% endif %}.jinja diff --git a/template/backend/src/{% if use_dishka %}dependencies{% endif %}/types/__init__.py.jinja b/template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/types/__init__.py.jinja similarity index 100% rename from template/backend/src/{% if use_dishka %}dependencies{% endif %}/types/__init__.py.jinja rename to template/{{ part_dir }}/src/{% if use_dishka %}dependencies{% endif %}/types/__init__.py.jinja diff --git a/template/backend/{% if 'kurigram_userbot' in backend_services %}sessions{% endif %}/.gitkeep b/template/{{ part_dir }}/{% if 'kurigram_userbot' in backend_services %}sessions{% endif %}/.gitkeep similarity index 100% rename from template/backend/{% if 'kurigram_userbot' in backend_services %}sessions{% endif %}/.gitkeep rename to template/{{ part_dir }}/{% if 'kurigram_userbot' in backend_services %}sessions{% endif %}/.gitkeep diff --git a/template/backend/{% if database == 'postgres' %}alembic.ini{% endif %}.jinja b/template/{{ part_dir }}/{% if database == 'postgres' %}alembic.ini{% endif %}.jinja similarity index 100% rename from template/backend/{% if database == 'postgres' %}alembic.ini{% endif %}.jinja rename to template/{{ part_dir }}/{% if database == 'postgres' %}alembic.ini{% endif %}.jinja diff --git a/template/backend/{% if database == 'postgres' %}migrations{% endif %}/env.py b/template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/env.py similarity index 100% rename from template/backend/{% if database == 'postgres' %}migrations{% endif %}/env.py rename to template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/env.py diff --git a/template/backend/{% if database == 'postgres' %}migrations{% endif %}/script.py.mako b/template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/script.py.mako similarity index 100% rename from template/backend/{% if database == 'postgres' %}migrations{% endif %}/script.py.mako rename to template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/script.py.mako diff --git a/template/backend/{% if database == 'postgres' %}migrations{% endif %}/versions/.gitkeep b/template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/versions/.gitkeep similarity index 100% rename from template/backend/{% if database == 'postgres' %}migrations{% endif %}/versions/.gitkeep rename to template/{{ part_dir }}/{% if database == 'postgres' %}migrations{% endif %}/versions/.gitkeep diff --git a/template/backend/.gitignore b/template/{{ part_dir }}/{% if layout == 'part' %}.gitignore{% endif %} similarity index 100% rename from template/backend/.gitignore rename to template/{{ part_dir }}/{% if layout == 'part' %}.gitignore{% endif %} diff --git a/template/backend/CLAUDE.md b/template/{{ part_dir }}/{% if layout == 'part' %}CLAUDE.md{% endif %} similarity index 100% rename from template/backend/CLAUDE.md rename to template/{{ part_dir }}/{% if layout == 'part' %}CLAUDE.md{% endif %}