- TypeScript 55.7%
- Go 18%
- HTML 17%
- Rust 3.6%
- CSS 2.5%
- Other 3.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| .opencode/agents | ||
| backend | ||
| deploy | ||
| design | ||
| docs | ||
| frontend | ||
| .gitignore | ||
| AGENTS.md | ||
| opencode.jsonc | ||
| README.md | ||
| START_PROMPT.md | ||
«Кабинет»
Backend сервиса состояния одного общего кабинета для трёх административно разрешённых участников: брони, посещения, heartbeat-сессии, события в реальном времени (WebSocket).
Исторический статус на завершении Stage 2: backend (Go / chi / pgx / PostgreSQL / Goose, REST + WS) и frontend (React 19 + Tauri 2) реализованы как рабочее приложение с реальным backend. Linux desktop проверен в подготовленном Docker-окружении (X11/Xvfb и headless Wayland); Windows не собирался и не запускался. Production backend развёрнут отдельно. Release-упаковка — последующая задача, её текущий статус приведён ниже; полный CI/remote не заявляется. Детали этапа — docs/frontend.md, docs/frontend-testing.md и docs/desktop-platform.md.
Desktop без dev server
Linux x64 release готов. На текущем Debian 13 из корня репозитория:
./artifacts/desktop/linux-x64/cabinet-desktop
Rust/Node.js и sudo для запуска не нужны. Необязательная установка .deb,
требования keyring и самостоятельная release-сборка — в
инструкции desktop.
Windows x64 также собран: для установки откройте
artifacts/desktop/windows-x64/Cabinet_0.1.0_x64-setup.exe; отдельный бинарник —
artifacts/desktop/windows-x64/cabinet-desktop.exe. Файлы локальные, не
опубликованы и не подписаны; запуск на Windows не проверен. Нужны WebView2
Runtime и Windows Credential Manager; подробности — в инструкции выше.
Важно о модели доверия
- Вход по Telegram-нику не доказывает владение Telegram: любой, знающий разрешённый ник, может войти, если у него нет живой сессии.
- Разрешённых ников ровно три; их добавляет администратор БД, не runtime-сервер.
- Production требует HTTPS; токены не передаются в query-строке и не пишутся в логи.
Документация
- docs/api.md — интеграция: аутентификация, идемпотентность, время, ошибки, эндпоинты, WebSocket.
- docs/deployment.md — конфигурация, БД, миграции, сид участников, права runtime-роли, локальный запуск, HTTPS.
- docs/production.md — runbook реального развёртывания
(host nginx + существующий shared-postgres):
deploy/compose.production.yml,deploy/nginx/. - docs/architecture.md — архитектурные решения.
- docs/verification.md — выполненные проверки и ограничения.
- docs/frontend.md — frontend Stage 2: конфигурация, API-клиент, auth/session, realtime, hooks, идемпотентность и ограничения API.
- docs/frontend-testing.md — browser E2E на реальном loopback backend, fixtures и native/manual-проверки.
- docs/desktop-platform.md — Tauri platform layer, окно, keyring, native HTTP/WS, prerequisites и команды запуска.
- OpenAPI — контракт REST и описание WS-фреймов.
Структура репозитория
backend/ Go-модуль backend (cmd/server, cmd/migrate, internal, migrations, api)
frontend/ React + TypeScript + Vite, Tauri 2 desktop, pnpm 10
deploy/ docker-compose.yml, compose.test.yml, .env.example, sql/ (сид/права)
docs/ документация (api, deployment, architecture, frontend, testing, desktop)
design/ визуальный прототип (не бизнес-правила)
.forgejo/workflows/ CI (backend.yml)
Быстрый старт (локально)
Порядок важен: сначала только БД (дождаться готовности), затем миграции/роль/сид, потом backend.
cp deploy/.env.example .env # заполнить ROOM_TIMEZONE, пароли
set -a; . ./.env; set +a # переменные .env в окружение
# 0) DSN ДО первого compose-вызова: ADMIN — на хост-порт (psql/goose),
# DATABASE_URL — на `db` (имя сервиса в сети Compose, для контейнера server).
enc() { printf '%s' "$1" | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read(), safe=""))'; }
export ADMIN_DATABASE_URL="postgres://${POSTGRES_USER}:$(enc "$POSTGRES_PASSWORD")@127.0.0.1:${PG_PORT}/${POSTGRES_DB}?sslmode=disable"
export DATABASE_URL="postgres://cabinet_runtime:$(enc "$RUNTIME_PASSWORD")@db:5432/${POSTGRES_DB}?sslmode=disable"
# 1) только БД (профиль local-db), дождаться healthy
docker compose -f deploy/docker-compose.yml --env-file .env --profile local-db up -d --wait db
# 2) инициализация: миграции, runtime-роль, права, сид ровно трёх
(cd backend && DATABASE_URL="$ADMIN_DATABASE_URL" go run ./cmd/migrate up)
psql "$ADMIN_DATABASE_URL" -f deploy/sql/create_runtime_role.sql
psql "$ADMIN_DATABASE_URL" -f deploy/sql/grants_runtime.sql
psql "$ADMIN_DATABASE_URL" \
-v nick1='<ник 1>' -v nick2='<ник 2>' -v nick3='<ник 3>' \
-f deploy/sql/seed_participants.sql
# 3) backend (DATABASE_URL из окружения указывает на `db`)
docker compose -f deploy/docker-compose.yml --env-file .env --profile local-db up -d --build server
# 4) готовность (БД + миграции + ровно 3 участника)
curl -fsS http://127.0.0.1:8080/readyz
Подробнее — в docs/deployment.md.
Разработка
cd backend
go mod download # зависимости (go.sum — lockfile)
gofmt -l . # форматирование
go vet ./... # статический анализ
go build ./... # сборка
go test ./... # тесты (требуют БД — см. ниже)
Интеграционные тесты используют изолированную тестовую БД:
docker compose -f deploy/compose.test.yml up -d --wait
(cd backend && TEST_DATABASE_URL="postgres://cabinet_test:cabinet_test@127.0.0.1:55432/cabinet_test?sslmode=disable" \
go test -race -count=1 ./...)
Frontend (pnpm 10; версия зафиксирована в frontend/package.json):
cd frontend
npx --yes pnpm@10.34.9 install
npx --yes pnpm@10.34.9 generate:api # OpenAPI -> src/types/openapi.d.ts
npx --yes pnpm@10.34.9 typecheck
npx --yes pnpm@10.34.9 test
npx --yes pnpm@10.34.9 test:platform # native adapter (node --test)
npx --yes pnpm@10.34.9 test:build-config # Vite env replacement smoke
CABINET_INTEGRATION_BASE_URL=http://127.0.0.1:18091 \
npx --yes pnpm@10.34.9 test:integration # только loopback test backend
npx --yes pnpm@10.34.9 build
npx --yes pnpm@10.34.9 preview # browser preview с /api proxy
Локальный запуск Linux
cd frontend
npx --yes pnpm@10.34.9 install
cp .env.example .env # необязательно; секретов в файле нет
npx --yes pnpm@10.34.9 tauri dev
По умолчанию используется публичный https://cabinet.zvario.tech; .env
содержит только endpoint/profile, без токенов и паролей. Пользователь сам
входит разрешённым ником, выданным администратором. Для Linux нужны
Rust/Tauri, GTK 3, WebKitGTK 4.1 и запущенный Secret Service (GNOME
Keyring/KWallet) в пользовательской DBus-сессии. На текущем host эти системные
зависимости отсутствуют; они установлены только в ephemeral Docker
toolchain-контейнере. Автоматических sudo apt install на host нет. Точные
Debian-пакеты, установка Rust stable и Secret Service требования приведены в
docs/desktop-platform.md.
VITE_BACKEND_URL/VITE_CLIENT_PROFILE читаются статическими свойствами
import.meta.env; npx --yes pnpm@10.34.9 test:build-config — обязательная
smoke-проверка замены env в production build. Автоматические тесты и E2E
выполняют мутации только на loopback fixture и никогда на публичном backend.
corepack pnpm на некоторых хостах сломан; используйте
npx --yes pnpm@10.34.9. Browser E2E использует desktop_one/desktop_two,
core integration — desktop_three; один fixture-аккаунт одновременно не
занимать. E2E запускается из frontend/:
PLAYWRIGHT_BROWSERS_PATH=/tmp/opencode/ms-playwright \
E2E_BACKEND_URL=http://127.0.0.1:33035 \
E2E_BACKEND_CONTAINER=cabinet-e2e-lead-deadline-final-backend \
npx --yes pnpm@10.34.9 exec playwright test --workers=1
Финальный результат lead на существующей fixture lead-deadline-final:
14 PASS за 32.1 s; cleanup fixture выполняется отдельно после handoff.
Для создания новой fixture одной командой см.
docs/frontend-testing.md.
CI (Forgejo Actions)
.forgejo/workflows/backend.yml: gofmt → go vet → go build → go test -race
(с изолированной PostgreSQL) → docker build. Публикация образа — опционально
(переменная PUBLISH_IMAGE) и только с веток main/master (pull_request
исключён), без автодеплоя.
Раннеры — self-hosted, два лейбла: docker-check (контейнерный исполнитель без
доступа к docker.sock) для проверок и docker-image (доверенный выделенный
исполнитель с Docker-демоном) для сборки/публикации образа. Untrusted PR-код не
выполняется на docker-image. Детали — в заголовке workflow.