Full-stack web application with Python FastAPI backend, Next.js frontend, and Telegram bot. The application uses Docker containers for deployment.
- Services:
api/FastAPI backend;web/Next.js 15 + TypeScript (dirs:src/app,entities,features,widgets,shared,i18n,styles);tg/Telegram worker;infra/Docker Compose;data/runtime storage. - Telegram bot:
tg/main.pyhandles webhook updates (proxied via/tg/), authenticates via/users/token/+/users/bot/withutmfrom/start, and replies with a WebApp button built fromWEB. Localized bot messages live intg/messages/{locale}.json(five locales only). - Localization: bundles in
web/messages/*.json; per-locale routing underweb/src/app/[locale]/**. All user-facing text must use locale files.
- Core Taskiq files live in
api/app/tasks/:broker.py,scheduler.py,system.py, and__init__.py. - Task definitions are grouped under
api/app/tasks/periodic/,api/app/tasks/scheduled/, andapi/app/tasks/jobs/. - Model change hooks/events live under
api/app/tasks/events/(handlers only). - Event system files live in
api/app/tasks/:event_base.py,event_registry.py,event_dispatcher.py,event_enqueue.py. - Taskiq workers/scheduler should import
api/app/tasks/registry.py(registers all tasks). - Import job tasks via
from tasks import <task_name>(notasks.jobs.*);api/app/routes/tasks/__init__.pyshould only import job tasks. - Event retries: failed model events are rescheduled with exponential backoff in
process_model_event, andretry_model_eventsdrains the fallback queuemodel_events:pendingevery minute.
Depending on the project that develops based on this template, the entities of this template can be reused as follows:
- spaces (switch environment: like Slack workspaces)
- posts (text-centred entity: articles, news, ...)
- products (sales unit: goods, apartments, ...)
- options (example for goods: product size variations on marketplaces, example for apartments: pricing plans)
- users (main clients of the service + admins)
The "tasks" feature is a reward checklist that grants users inner coins after verification.
- Model:
api/app/models/task.py(taskscollection). Active tasks usually have nostatusfield because ConSys strips defaults;status=0disables a task. Optionalexpired(unix seconds) hides tasks for users and blocks claiming. - Completion state: stored in
UserLocal.tasks(api/app/models/user.py) as a list of completed task ids; user balance isUserLocal.balance. - Routes:
POST /tasks/get/(api/app/routes/tasks/get.py): user list (active + not expired) with derivedstatus(1/3) and link formatting; admin list with{"admin": true}returns raw definitions (requiresstatus>=6).POST /tasks/check/(api/app/routes/tasks/check.py): runsverify.<key>.check(user_id, params)and, on status3, awardsrewardand persists completion.POST /tasks/save/(api/app/routes/tasks/save.py): admin-only create/update; audited viaTrackObject.TASK.
- Verify modules live in
api/app/verify/:simple: always succeedschannel: requiresparams.chat_idand aUserLocal.social_user(Telegram user id)invite: requiresparams.countand checks referral count
- User page:
web/src/app/[locale]/tasks/page.tsx(mobile nav entry only, header unchanged). - Admin page:
web/src/app/[locale]/admin/tasks/page.tsx(CRUD + enable/disable). - Localization:
navigation.tasks,tasks.*,admin.tasks.*. ICU gotcha: to show a literal{}in messages, quote it like'{}', otherwisenext-intlthrowsINVALID_MESSAGE: EMPTY_ARGUMENT.
- Backend:
POST /users/frens/returns the current user's frens list (sorted by balance) with relation labels andreferral_link. - Referral key:
UserLocal.linkis generated viaencrypt(user.id, 8)and decoded withdecrypt()inroutes/users/auth.update_utmto resolve referrers. - VK Mini Apps: share links should use
#utm=<referral>(hash) so the frontend can map it toutmon session init;vk_refis reserved by VK. - Frontend:
web/src/app/[locale]/social/page.tsxrenders the frens list and usesnavigation.frens+social.*i18n; mobile bottom bar includes the frens entry linking to/social.
.envdefines one canonical environment variableENVwith allowed values only:local— local developmenttest— local unit tests and CI/CD pipeline testsdev— staging serverpre— pre-production (20% production-like environment)prod— production server
ENVis loaded inapi/,web/, andtg/containers.- Local/dev setup: copy
.env.example→.env. - VPS deploy setup: do not create/fill
.envmanually on server. - VPS
.envis generated by pipeline frominfra/compose/.env.list. - CI variables/secrets must be fully populated from
.env.example(GitHub Actions vars+secrets or GitLab CI/CD variables). - Some keys in
infra/compose/.env.listare intentionally absent in.env.examplebecause pipeline generates them during deploy (for example:COMMIT_SHA,RELEASE,IMAGE_TAG,REPOSITORY,REGISTRY,STACK_NAME). - Normalize
ENVto lowercase. If value is empty/invalid/unrecognized, fallback totest(default). - Environment access must stay unified:
- Backend: use
cfg("env") - Pipelines / Docker / Make / infra: use
ENV - Frontend: use
NEXT_PUBLIC_ENV(mirrorsENV)
- Backend: use
- Do not add alternative environment selectors such as
NODE_ENV=production,ENV_NAME,APP_ENV,STAGE, or similar aliases for runtime environment selection. - Base URLs: server-side requests must use
http://api:5000/(internal Docker network); client-side requests must useNEXT_PUBLIC_APIthrough nginx; do not add separate API base URL variables. - Detect environment via
NEXT_PUBLIC_ENV(mirrorsENV). - Web stack builds use Node 24 (Docker + compose test) and Next 16/React 19; keep local runtime and
@types/nodealigned when bumping.
- Explain the plan (brief): research, make strategy and all steps of changing. compliance with the rules of the repository and the specifics of custom libraries and components
- Development: Follow the DevOps Principles & Backend Principles & Frontend Principles
- Minimal, focused diffs: change only what's necessary; Make minimal changes in relevant files & rows only
- Page blocks → components: In
web/src/app/**/page.tsx, extract each major UI block/section into a named component/function (keeppage.tsxorchestration-focused; avoid huge JSX returns). - Concise naming: prefer short, laconic labels (e.g.,
paysoverpayments/charges,adoveradvertisement/promotion,postoverarticle,tasksoverschedules,infraoverinfrastructure) - Cursor pointer everywhere: All clickable elements/blocks/links/pickers/sliders must explicitly set
cursor-pointerfor clear affordance - Dialog affordance: Dialog close buttons AND the dimmed overlay/backdrop used to close dialogs must have
cursor-pointer - No duplicate API calls: guard client-side fetch effects with stable fetch keys/in-flight refs so Strict Mode doesn’t trigger the same request multiple times (one request per dataset)
- Documentation: Write documentation directly in code files as comments and docstrings, not as separated files (No new .md files to describe logic, usage, or implementation details; No example .json files to show data structures or logging formats)
- Required fields UX: For all forms mark required inputs with
*and highlight missing/invalid required fields with a red focus/outline when the API returns validation errors (e.g.,detailvalue). Keep visual feedback consistent across the app. - Popups/Toasts: Emit only one localized toast per error; map backend
detail/field keys to translated messages and surface the exact field causing the issue (no duplicate global+local toasts). - Error responses: Backend errors follow
{"status":"error","error":"ErrorWrong","detail":"id"}shape. Frontend popups must show the rawdetailvalue (field/key) once, and optionally add a concise localized hint if available. Reuse this rule across all routes/components (not just categories) to avoid multiple generic popups. - Backend error formatting: Raise
BaseError(txt)(fromconsys.errors) for any user-facing API error soapi/app/services/errors.pymiddleware returns HTTP 400 with{"status":"error","error":<ErrorClass>,"detail":txt}. Do not leak stack traces/500s for validation/processing failures (including uploads); wrap unexpected processing errors intoBaseErrorwith a cleandetail. - Dialogs must scroll: Large popups (forms/modals) must fit on mobile and small screens with max-height constraints and internal
overflow-y-autoso content is scrollable without breaking layout. - Mobile adaptive: Every screen, table, and control layout must stay usable on small/mobile widths; add horizontal scroll containers for wide tables instead of letting them overflow.
- App shell layout:
layout.isApp(selectIsApp) marks in-app clients (tg/vk/max). WhenisAppis true, renderMobileBottomBaron all viewports and hideHeader/Footer; when false, always showHeader/Footerand never show the bottom bar. KeepisAppsynced to runtime detection viashared/lib/app.ts(getClientNetwork,isApp) and avoid breakpoint-based gating for app shells (Telegram detection lives inshared/lib/telegram.ts; VK detection usesshared/lib/vk.tswithvk_user_id+signparams; MAX detection usesshared/lib/max.tswithwindow.WebApp.initData). - Shared translations first: Use existing
system.*translation keys for shared labels (loading, refresh, common actions) instead of introducing feature-specific duplicates; migrate simple words from feature scopes tosystem.*when touching those areas.- When adding new locale strings, ensure non-English locales are translated (avoid copy-pasting English into
ru/es/ar/zh).
- When adding new locale strings, ensure non-English locales are translated (avoid copy-pasting English into
- No duplicated UI/i18n: If multiple screens/forms need the same control or helper text, extract a shared component in
web/src/shared/ui/and move the strings tosystem.*(delete feature-scoped duplicates). - Unit suffixes: Show measurement units using right-side labels/suffix segments on inputs (e.g., %, kg, cm); keep left labels clean.
- Number inputs: Hide browser stepper arrows and prevent scroll-wheel value changes; use shared Input defaults or equivalent handlers for any custom number fields.
- Allow clearing inputs: Form fields (including numeric inputs) must allow users to clear the value with Backspace/Delete before retyping; do not force immediate fallback values while typing.
- Verify APIs with curl: When adding or debugging endpoints, hit them with
curl(use bearer tokens provided in examples when available) to confirm responses and contracts before handoff. - Add relevant information to this AGENTS.md file; Update the main README.md if necessary
- API sanity via curl: When debugging/adding flows, hit backend endpoints with curl locally (using provided bearer tokens when available) to validate responses and fix errors before shipping.
- Pre-handoff checks: Always run
pnpm run lintandpnpm run build(ormake lint-web) yourself before finishing a task; fix any errors locally. If sandbox/network blocks them, explicitly note the failure reason and keep TypeScript/routing types clean (use typedredirect/pushobjects aligned withweb/src/i18n/routing.ts). - Track actions with enums: Use
Track.log+TrackObject/TrackActionenums fromapi/app/models/track.pyfor every audited change (objects: user, post, product, category, comment, space, payment, session, system; actions: create, update, remove, search, view, disconnect). Always passrequestto capture context (source/network, status/roles, locale, user agent, ip viarequest.state.ip, url, token) and provideparamswith compact change sets only (no before/after echoes) via{"id": entity.id, "changes": format_changes(entity.get_changes())}. Example:Track.log(object=TrackObject.POST, action=TrackAction.UPDATE, user=request.state.user, token=request.state.token, request=request, params={"id": post.id, "changes": format_changes(post.get_changes())}). The admin feed uses/admin/activity/(default 20 last events) with filters for user, ip, object/action, and date range—keep Track entries consistent. - User profiles fetch: When you need user login/name/surname in responses or UI feeds, never stuff them into Track params. Use
fetch_user_profiles(ids)fromapi/app/models/user.pyto fetch global users first and overlayUserLocalfields in one batched request; reuse this helper across routes instead of ad-hoc UserLocal queries. - Reuse cards/items: Always reuse existing card/item components for repeated contexts (landing listings, related/recommended blocks, similar products, etc.)—e.g., use the shared PostCard for any post teasers (landing, related posts) and the shared product card for similar products instead of creating new variants.
- Follow FSD Architecture: respect Feature-Sliced Design layers and import rules
- Never hard-code secrets or credentials; never read or write
.env,secrets/, or CI secrets - Ask before destructive or external actions (network, DB migrations, Docker,
git push, etc.) - Files & Paths Not To Touch:
.env*,secrets/**. - Translation rules:
- Reuse common system translations for generic actions (Add, Save, Update, Edit, Delete/Remove, Cancel, Refresh, Loading, etc.) instead of creating duplicated or feature-scoped keys.
- Common choices (Yes/No and similar simple words) must use the shared
systemlocale keys (system.yes,system.no) instead of feature-scoped duplicates. - Non-English locales must be properly translated (no English copy/paste for
ru/es/ar/zh); add missing keys to all locales together. - Centralize reusable option labels (e.g., entity forms/types) under shared namespaces rather than duplicating in feature scopes.
- API routing objects: When calling backend routes, use the existing typed API helpers and common endpoints (
/posts/get|save|rm,/categories/get|save|rm,/products/get|save|rm, etc.) instead of ad-hoc URLs or duplicated schemas. - Typed frontend routing: Use
useRouterfrom@/i18n/routingand passRouteHref = Parameters<typeof router.push>[0]only; avoid raw string concatenation. Dynamic routes must use{ pathname: '/spaces/[link]', params: { link } }style objects. Keepweb/src/i18n/routing.tspathnamesupdated before adding navigation targets so unions include new routes. Navigation lists should type theirpathfields asRouteHrefto prevent loose strings.
✅ All endpoints use async/await patterns
✅ Pydantic models with field descriptions and examples for schema generation
✅ Response models specified in FastAPI decorators (response_model=)
✅ OpenAPI tags for endpoint organization
✅ ConSys models for MongoDB operations
✅ Structured logging with loguru
✅ Type hints on all functions and variables
✅ Error handling with proper HTTP status codes
✅ Tests cover all critical paths
✅ No sensitive data in logs
✅ Generate TypeScript schemas after API changes (pnpm run generate-schemas)
✅ All text uses i18n keys (no hardcoded strings)
✅ Interactive elements have icons + cursor-pointer + hover states
✅ ALL icons imported from @/shared/ui/icons - no direct react-icons imports
✅ Icons follow priority: fa6 → bi → hi (no inline SVG and Emoji)
✅ Dates use standardized format (%dd.%mm.%YYYY)
✅ Components work in light & dark themes
✅ Consistent border-radius (.75rem vs 1rem)
✅ No border classes used (shadows/backgrounds only)
✅ FSD architecture with correct import layers
✅ TypeScript strict mode compliance
✅ Use generated TypeScript schemas from @/generated/api/schemas
✅ No hardcoded API types - import from generated schemas
✅ Responsive design with Tailwind CSS
✅ Redux Toolkit for state management
✅ Error handling with toast notifications
✅ Run pnpm run build to validate FSD structure
✅ Run pnpm run lint for code quality
- Frontend Access: Web App available at
http://localhost/and API Access: Backend API available athttp://localhost/api/when containers are running (usinginfra/nginxinserverservice incompose.local.yml)
- Make (Makefile)
- all main commands are stored here
- NGINX (infra/nginx/)
- use http://localhost/ for local frontend test and http://localhost/api/ for local api requests (managed by compose.local.yml)
- prod WebApp embed: ensure CSP
frame-ancestorsallows Telegram Web (web.telegram.org,*.telegram.org,t.me) and strip upstream CSP/X-Frame-Options in proxy
- Docker & Docker Compose & Docker Swarm (infra/compose/)
- all compose files are overwrites of the base
infra/compose.yml(for example of run:docker compose -f compose.yml -f compose.local.yml)
- all compose files are overwrites of the base
- Database: MongoDB with custom ConSys library for ODM
- API-first and POST-only JSON (Stripe/Twitter style). Endpoints are JSON-RPC-ish: POST body carries action payload; responses have consistent success/error envelopes; avoid form/multipart unless required.
- Real async: async/await end-to-end with non-blocking clients; try/catch with domain error mapping; avoid blocking I/O.
- Keep it lean: no unnecessary wrappers, deep inheritance, or extra deps; keep schemas/models near routes.
- Typing: prefer modern PEP syntax (for example:
T | None,list[str],dict[str, Any]over legacyOptional/List/Dict). - API Documentation: OpenAPI/Swagger auto-generated; Contracts: Swagger/OpenAPI is source of truth; define Pydantic request/response schemas; auto-generate TS types + client (e.g.,
openapi-typescript+openapi-fetch) so drift fails lint/build. - Custom libs:
consysORM (docs/packages/consys.md),libdevhelpers (docs/packages/libdev.md),userhub(auth,docs/packages/userhub.md),tgio(Telegram helpers,docs/packages/tgio.md). Check docs/README before changes. - Authentication: JWT tokens with FastAPI security
- Caching: Redis for session storage and caching
- Logging & alerts: Structured loguru logging; error/perf reporting via Sentry (
api/app/services/sentry.py). - Log format: Containers write JSON logs to stdout/stderr only (no file sinks). Required fields:
project,service,env,version,level,trace_id/request_id,msg,error.stack(if present). Keep high-cardinality data as fields inside the JSON payload, not as log labels. - Unified pipeline: API/TG must emit logs through Loguru only; the same Loguru records must feed stdout JSON (Alloy -> Loki -> Grafana), send
error/critical/exceptionrecords to Sentry, and send Telegram alerts forlog.important(...)and any log call withsilent=False(defaultsilent=True). - Logger import path (API): Import the final logger from
api/app/lib/__init__.py(from lib import log) in application modules; only logging/sentry infrastructure modules may import fromservices.logging. - Logger call contract: Do not pass custom
error=objects to logger calls. Raise/handle exceptions normally and uselog.exception(...)or log insideexceptblocks so traceback is attached automatically. - Logger message style: Use f-strings in logger calls (for consistency with project style), not Loguru
{}-placeholder formatting. - Logger bootstrap (API):
setup_logging(project=None, service=None, env=None, version=None, level=None, notify_token=None, notify_chat=None)must receive Telegram notify credentials from the caller (lib/__init__.py); if omitted, notification sink stays disabled. - Notify transport (API): Keep Telegram notify transport helpers in
api/app/services/notify.py;api/app/services/logging.pymust call that module instead of implementing HTTP notify requests inline. - Telegram notify formatting: Notifications must use
MarkdownV2parse mode and escape all dynamic values (message,extrakeys/values, tags, ids, trace/user data) to prevent Markdown parsing errors. - Logger methods and params:
- Standard methods:
log.trace|debug|info|success|warning|error|critical(message, *args, tags=None, silent=True, extra=None) - Exception methods:
log.exception(message, *args, tags=None, silent=True, extra=None)(always attaches traceback) and aliaslog.except_(...) - Important method:
log.important(message, *args, tags=None, silent=False, extra=None)(INFO log + Telegram notify typeIMPORTANT) - Request method:
log.request(message, *args, tags=None, silent=True, extra=None)(INFO log + Telegram notify typeREQUESTwhensilent=False) - Generic method:
log.log(level, message, *args, tags=None, silent=True, extra=None) - Helpers:
log.catch(...)decorator/context andlog.bind(**kwargs)for bound logger context - Param meanings:
message(log text),*args(optional payload shortcut),tags(string label or list of string labels only; no key/value),silent(whenFalse, send Telegram notify),extra(key/value payload attached to log and Sentry extras) - Payload shortcut: when
messagehas no{}placeholders and one positionaldict/list/tuple/setis passed, it is treated aspayloadautomatically. - Extra rendering: payload from
extra(or payload shortcut) is appended after the main log message text in JSONmsgand is also attached to Sentry aspayload. - Telegram notify types and symbols:
DEBUG=💬,INFO=🟢,WARNING=⚠️,ERROR=❗️,CRITICAL=‼️,IMPORTANT=✅,REQUEST=🛎.
- Standard methods:
- Logger usage examples:
- Basic:
log.info(f"Restart server")
- With payload (no notify):
log.warning("Invalid token", {"url": url, "token": token})
- With tags:
log.error(f"Payment failed for order {order_id}", tags=["billing", "payments"])
- With explicit key/value payload via
extra:log.info(f"Sync finished", extra={"count": total, "duration_ms": duration_ms})
- Force Telegram notify from non-
importantmethod:log.error(f"Provider timeout for {provider}", silent=False, tags="provider", extra={"provider": provider})
- Important event (notify by default):
log.important(f"Manual moderation required for user {user_id}", tags="moderation", extra={"user_id": user_id})
- Request event:
log.request(f"Users export requested", silent=False, tags=["admin", "export"], extra={"requested_by": user_id})
- Exception handling:
try: await run_job() except Exception as exc: log.except_(f"Job failed: {exc}", extra={"job": job_name})
- Basic:
- Frontend Sentry: Web app uses
@sentry/nextjs(web/sentry.client.config.ts,web/sentry.server.config.ts,web/sentry.edge.config.ts) withNEXT_PUBLIC_SENTRY_DSN. - Testing: pytest with async test support
- Background Tasks: Celery with Redis broker
- Auth/Session flow (FE+BE):
- Guest bootstrap: On first client load
SessionInitializergenerates a client token (UUID/random), calls/users/token/with network=web, utm + browser tz/langs, receives JWT. Tokens persist inlocalStorageassessionToken(client) andauthToken(JWT). API client automatically addsAuthorization: Bearer <authToken>. - JWT contents: encodes
token(session id),user(id or 0),status(rights),network(provider id). BackendAccessMiddlewarevalidates JWT and setsrequest.state.token|user|status|networkfor all routes. - Rights/status: Status codes follow UserHub (0 deleted, 1 blocked, 2 unauthorized, 3 authorized, 4+ elevated incl. moderators/admins up to 8 owner). Whitelist in
AccessMiddlewareallows public POSTs (token creation, content fetch/save as configured); others require valid JWT. - User storage (BE): Users and tokens live in core UserHub (
userhublib), plus project-local overlayUserLocal(api/app/models/user.py) for per-project fields (balance, premium, mailing, wallet, locale, referrer/frens, utm, tasks/social cache). Creation/auth flows go throughroutes/users/auth.py,routes/users/token.py,routes/users/social.py,routes/users/app/tg.pyusinguserhub.auth/token;UserLocalis created/updated during auth (on referral/locale changes). Seedocs/packages/userhub.mdfor contract, validation, and status meanings. Local users are fetched in routes viaUserLocal.get(request.state.user)to apply project-specific data/roles. - Login: FE dispatches
loginWithCredentials→/users/auth/with login/password/utm, stores returned JWT inauthToken, updates Reduxauthslice with user profile. API requests immediately use new JWT. - Telegram Mini App auto-auth:
TelegramAuthInitializercheckswindow.Telegram.WebApp.initData+ user; if present and user not set, calls/users/app/tg/withinitData+ utm to auth/link Telegram session and rotate JWT. - VK Mini App auto-auth:
VkAuthInitializerchecksvk_user_idin URL params; if present and user not set, calls/users/app/vk/withwindow.location.href+ utm to auth/link VK session and rotate JWT.VkBridgeInitializercallsVKWebAppInitvia VK Bridge when running in VK. - MAX Mini App auto-auth:
MaxAuthInitializercheckswindow.WebApp.initData; if present and user not set, calls/users/app/max/withinitData+ utm to auth/link MAX session and rotate JWT. - Social callback: External providers (Google/Telegram/etc.) redirect to
/[locale]/callbackwithcode; page infers provider, calls/users/social/withcode+ utm, stores JWT/user, and redirects to previous path or profile. - Logout: FE
logoutthunk hits/users/exit/, clearsauthToken, resets auth slice, and re-runs guest token bootstrap. - API signing: All FE API requests flow through
apiClientwhich readsauthTokenfrom storage, setsAuthorization, and uses global error handling/toasts. - Head scripts for TMA: Locale layout loads Telegram WebApp script
beforeInteractivepluseruda(dev) for Mini App debugging.
- Guest bootstrap: On first client load
- TypeScript
- Tailwind CSS + Radix UI (shadcn/ui)
- Redux Toolkit
- Next.JS + SEO
- Layer Placement: Basic UI →
shared/ui/, Complex compositions →widgets/, Feature-specific →features/*/ui/ - FSD Import Rules: Higher layers can import from lower layers only:
app/→widgets/,features/,entities/,shared/widgets/→features/,entities/,shared/features/→entities/,shared/entities/→shared/onlyshared/→ no internal dependencies
- Adaptive: Mobile-first; build single adaptive components instead of breakpoint forks. Honor theme tokens and reuse Radix primitives.
- Themes: system (default) / light / dark; components should honor tokens.
- Path alias
@/*→web/src/*. Strict TS; PascalCase components; hooks asuseThing; slices infeatures/*/stores/*Slice.ts. Keep barrels small. - Use toasts / popups for feedback: errors/warnings/success/info should use app toasts/dialogs, not
alert()or raw text - Icons: Centralized icon management: ALL icons MUST be imported from
shared/ui/icons.tsxfile, never directly fromreact-icons; React-icons priority system**: usereact-iconswith priority order: 1.fa6(Font Awesome 6), 2.bi(Bootstrap Icons), 3.hi(Heroicons). Never use inline SVG - Special symbols vs icons: use Unicode symbols (©, ®, ™) as text characters, not icons with backgrounds
- Accessibility first: proper aria labels/roles, focus states, keyboard nav; no color-only affordances
- For any entity, cover the full pipeline if needed: create/update the DB model, expose/extend the route endpoint and typed API routes, ship the frontend client page, set access levels, and surface it in header sections, landing blocks, and the admin panel page.
- After finishing any feature, run lint and build, start the frontend, and recheck Docker console logs for new errors.
- Детальные правила со сниппетами: основы (
docs/components/foundations.md), контейнеры/страницы (docs/components/layout.md), кнопки (docs/components/buttons.md), формы (docs/components/forms.md), списки (docs/components/entity-lists.md), фидбек (docs/components/feedback.md).
- Theme-aware: Support both light & dark themes via CSS variables/tokens
- No borders: Use shadows for big / outer elements, backgrounds for small/inner elements
- Small / inner elements:
rounded-[0.75rem](buttons, inputs, avatars, icons) - Big / outer elements:
rounded-[1rem](boxes, containers, cards, sections)
- Interactive shadows: Big/outer components use Card-style shadow with hover effects:
- Shadow:
shadow-[0_0.25rem_1.5rem_rgba(0,0,0,0.12)] - Transition:
transition-all duration-300 ease-[cubic-bezier(0,0,0.5,1)] - Hover effect:
hover:scale-[1.01](subtle scale animation)
- Shadow:
- Standardized Format:
%dd.%mm.%YYYY(e.g., "01.01.2024") everywhere. No other date formats allowed
- Interactive Elements (Buttons/Links): Icon color MUST match text color - no separate icon coloring. Use
IconButtonwithresponsive={true}for adaptive text hiding (below 1280px shows icons only, 1280px+ shows icons + text). - Standalone Icon Containers: Independent icons (avatars, PageHeader, category icons) MUST use rounded square containers (
rounded-[0.75rem]). Icon at full opacity, background at low opacity. - Default Icon Styling:
bg-muted text-muted-foregroundfor neutral/default standalone icons. - Colored Icon Pattern:
bg-{color}-500/15 text-{color}-600 dark:bg-{color}-500/20 dark:text-{color}-400for themed icons. - Opacity Standards: Background opacity 15% (light) / 20% (dark), icon/text at full opacity for proper contrast.
- Examples (
web/src/shared/ui/icons.tsx): Save <-SaveIcon<-FaFloppyDisk
- Destructive badges: Red/danger badges (e.g.
Badgevariantdestructive) MUST render with white text (usetext-white; avoid black text on red backgrounds).
- Use
widgets/feedback-systemcomponents for all user feedback. - Map severities to variants:
success,error,warning,info. - Never use
window.alert()for UX feedback. - Import:
import { ToastProvider, useToast } from '@/widgets/feedback-system' - Success actions (create/save/delete) must show green success toasts; failures must show red error toasts. Use shared toast helpers (
useToastActions) consistently.
- Полные правила и примеры по компонентам см.
docs/components/foundations.md,docs/components/layout.md,docs/components/buttons.md,docs/components/forms.md,docs/components/entity-lists.md,docs/components/feedback.md.
- Wrap ALL content in
Boxcomponent from@/shared/ui/box - Do NOT wrap card grids (PostCard/ProductCard or similar card lists) in an extra
Boxbecause cards already provide the boxed surface
- Use for ALL pages/sections with proper icon color system
- Import:
import { PageHeader } from '@/shared/ui/page-header' - Universal Usage: EVERY page, demo component, admin section, content area MUST use PageHeader
- Placement Rule: PageHeader MUST be in page body, NEVER inside Box components
- Structure:
<div>→<PageHeader />→<Box>content</Box> - Never Nest: ❌
<Box><PageHeader /></Box>- PageHeader should be outside and above Box - Icon: Square colored container,
size={24}, no border,rounded-[0.75rem] - Title: SEO-optimized page title
- Description: Additional context/breadcrumbs
- Actions: Button groups on right side
- Wide tables must be wrapped in horizontal scroll containers (
overflow-x-autowith sensiblemin-width) to avoid clipping on mobile; do not let columns overflow their parent.
- Import:
import { Button } from '@/shared/ui/button',import { IconButton } from '@/shared/ui/icon-button',import { ButtonGroup } from '@/shared/ui/button-group' - IconButton Rule: ALL buttons/links MUST start with icon, then localized text
- Responsive Pattern: Use
responsive={true}for adaptive behavior (icon-only <1280px, icon+text ≥1280px) - Required:
cursor-pointerstyling, clear hover/focus/active states - Icon Color: Icon color MUST match text color - no separate icon coloring
- Purpose ButtonGroup: Group related buttons with shared borders and visual connection
- Use Cases ButtonGroup: Edit+Delete, Save+Cancel, Upvote+Downvote, action clusters
- Colors: Use semantic colors (red=delete, green=add, etc.)
- Import:
import { ThreeColumnLayout } from '@/widgets/three-column-layout' - Adaptive: Auto-adjusts based on provided sidebars
- Sticky: Sidebars use
sticky top-20(80px offset for header) - Usage:
leftSidebar={<>multiple widgets</>}rightSidebar={<>widgets</>}
- Import:
import { SidebarCard } from '@/shared/ui/sidebar-card' - Rule: ALL sidebar widgets MUST use SidebarCard for consistent styling
- Header: Optional
title+iconprops (icon size 20px) - Content Spacing:
sm(space-y-4),default(space-y-6),lg(space-y-8) - Never: Manual
<div className="flex items-center gap-2">headers
- Imports:
import { EntityManagement, EntityRow } from '@/shared/ui/entity-management' - Layout: Always a single outer
Boxfrom EntityManagement; rows use full-widthEntityRowwith first line#id + title + /url + badges, second line dot-separated metadata. - Left slot: Pass icons/images/expanders via
leftSlot; do NOT add extra wrappers that break alignment. - Right actions: Provide button groups via
rightActions; alignment comes from EntityRow (no extra flex wrappers needed). - Second-row items: Pass an array of objects
{ icon, keyLabel?, value };EntityRowrenders them as pills with icon + value and showskeyLabelon hover (titleattr). Keep values short; let EntityRow handle pill styling. - Reuse: Products, posts, categories admin lists must reuse EntityRow instead of custom row markup.
- Row chips: Second-line metadata must use the unified chip pill (
inline-flex items-center gap-2 rounded-[0.75rem] bg-muted px-2 py-1) with icons (mail/phone/globe/category/price/etc.) instead of plain text so list rows match the admin activity style.- Example:
secondRowItems={[ { icon: <GlobeIcon size={12} />, keyLabel: t('fields.locale'), value: locale }, { icon: <CategoriesIcon size={12} />, keyLabel: t('fields.category'), value: categoryTitle }, ]}
- Example:
- Component reuse: use the same form component for create/edit modes and the view variation for display/preview in those modes to avoid divergence.
- Post editing screens must follow the in-place layout: full-width title input (placeholder shows the field name), then a row with image upload on the left and locale + category pickers plus description on the right (picker labels inline with values), and a large WYSIWYG/text area beneath. Inputs should present labels inline with values as in the reference design.
- Picker/input styling: Selects/inputs used in edit mode must share the same muted background style as textareas/main inputs, without borders, and show a pointer cursor on hover.
- Picker font rule: Picker inputs must use the same font weight/size/color styling as other inputs and place the key label inline with the value; hover must show pointer.
- FileUpload usage: pass
fileDatawithtype: 'image'or provide an image URL invalue; setwidth=\"w-full h-full\"when you need full-height containers alongside form columns. - File uploads: On selecting an image, call
/upload/via the API, use the returned URL in form state, and keep previews in sync for post and profile editors. - Inline label rows: For compact inputs (e.g., category, price, login, phone), place the label text inside the input row (left prefix) with the same muted background as the input (bootstrap-style inline add-on, no alternate color), a subtle divider if needed, borderless controls, and pointer-only on interactive elements.
- Form components (use these first):
- Inputs/Textareas:
@/shared/ui/input,@/shared/ui/textarea. Both use muted backgrounds (light/dark) and no borders/shadows; do not wrap with extra borders. Keep placeholders localized. For inline rows keep label + control in the same muted surface. - Icon keys (FontAwesome):
@/shared/ui/icon-key-input— reuse this component (no per-feature duplicates), stores key-only values (e.g.,home) and includes a browse link. - Selects/Pickers:
@/shared/ui/select(Radix) with our trigger styling; avoid native<select>except insideInlineSelectwrappers that mirror the same muted background and custom chevron (as used in product form). - File uploads:
@/shared/ui/file-uploadfor single image/avatar;@/shared/ui/multi-file-uploadfor multiple images (products, galleries). Always upload via/upload/and store returned URLs in form state. - Rich text:
@/shared/ui/editor(CKEditor lazy-load wrapper) for WYSIWYG fields; keep placeholders/i18n and honor the light edit surface it applies. - Inline rows: follow the product/profile pattern—muted row, left label segment with divider, borderless controls, pointer cursor on interactive selects/toggles.
- Copy/suffix actions: share/copy fields must be a single muted row with a read-only input and a right-side icon+label button (suffix) inside the same surface; no extra borders, keep cursor-pointer on the action.
- Units: keep measurement units (%, kg, cm, etc.) as right-side suffixes inside the input row; do not mix units into the left labels.
- Grouped fields: when fields are tightly related (e.g., name+surname, country+region+city), group them into a single inline row on desktop with shared muted background/dividers and stack vertically on mobile.
- Toggles/checkboxes: prefer row-level click targets (as in product form) with 20px checkboxes, rounded corners, and full-row cursor-pointer/hover.
- Inputs/Textareas:
- Make plan of changing: Explain the plan (brief)
- Update objects: Define models + DB migrations.
- API Tests First: Draft corner/edge-case backend tests (aim for 100% on models/routes/functions; add fixtures).
- Backend Implementation: Implement routes/functions/scripts.
- Documentation: Update docs and generate TS schemas.
- Frontend Implementation: Build frontend components/pages; verify styling/locales/themes/access/mobile.
- Frontend Tests: Add frontend tests.
- Run tests and linters: Avoid extra layers unless justified.
- Frontend prod parity: Before merging, rerun frontend lint/build and fix all prod build errors (typed routes, lint violations). Ensure
pnpm run build(or CI web-check job) passes without errors. - Local verification gate: Always run
pnpm run lintandpnpm run build(ormake lint-web/make lint-web-fix+ build) before handing off; if blocked by sandbox/network (e.g., Google Fonts fetch), report the failure reason explicitly in the summary and keep the TypeScript check clean.
- On any dependency change, regenerate lockfiles: backend via
uv lock --python 3.13inapi/; frontend by refreshing the JS lockfile (pnpm installinweb/to updatepnpm-lock.yaml).
- Frontend (
web/):pnpm install;pnpm run dev;pnpm run build;pnpm run start;pnpm run lint. - New lint helpers:
make lint-web(check) andmake lint-web-fix(auto-fix) run from repo root. - Pre-commit hook: husky runs
corepack pnpm --dir web run lint:fix; runpnpm installinweb/(orpnpm run prepare) to ensure hooks are installed if missing. - Stack (repo root): unified commands only —
make up,make down,make logs,make status(behavior depends onENV). - Tests:
make testfor API + web in compose;make test-apiormake test-webindividually.
- API tests live in
api/tests(pytest). Add alongside new routes/models; nametest_*; share fixtures intests/conftest.py; target high coverage. - Frontend currently lint/manual; if adding tests, place under
web/src/**/__tests__and wire intomake test-web. - Note skips/flakes in PRs.
- Commits: short, imperative (
Fix local API requests). Branches:feature/*,bugfix/*. PRs: summary, linked issue, commands run, screenshots/GIFs for UI changes, and env/migration updates.
- Frontend now auto-creates a guest session on client load via
SessionInitializer(Redux-persistedsessionslice), calling/users/token/and storing tokens inlocalStoragekeysauthToken/sessionToken. - Auth flow restored with modal chooser (email/Google/Telegram) using Redux
authslice and/users/auth/; logout hits/users/exit/then reinitializes guest session. - Auth popups should place content directly in the popup (no extra Box wrappers that add inner shadows/backgrounds); keep buttons stacked and titles concise.
- Product pricing now lives inside
options(each option hasprice,discount_type/discount_value,in_stock,rating,images, and extrafeatures/attributes); product-levelpriceFrom/finalPriceFromaggregate the cheapest option; base specs stay in sortedfeatureslist with{key, value, value_type}items. - Spaces: new
Spacemodel/routes/spaces/get|save|rm(link viaencrypt(id, 5)), attachments stored inUserLocal.spaces, shared edit form at/spaces/<link>+ admin list/selector.