Бэкенд учебной платформы для курса по Go. Прод-инстанс — https://kroexov.webhop.me/apprentice/: ученики регистрируются, ментор/админ ведёт их по этапам курса, выставляет баллы и отмечает прочитанные материалы.
Сервис обслуживает один курс с несколькими когортами (сейчас «Сезон 1 · Когорта весна-лето 2026»). Кандидаты двигаются по фиксированному списку этапов; на каждом этапе ментор ставит балл (1..maxScore) и кандидат переходит на следующий. Параллельно ведётся учёт прочитанных теоретических материалов.
Скриншоты лежат в docs/readme-img/. Сам фронт — в отдельном приватном репозитории; он потребляет публичный TS-клиент (docs/api.ts), сгенерированный из этого бэкенда.
Канбан-доска со всеми этапами курса в колонках. На карточке кандидата — аватар с инициалами, ник, текущая сумма баллов / максимум, прогресс-баром обозначены пройденные этапы, рядом — короткий ярлык текущего этапа (например, MR #1), плашка статуса (готово), ссылка на сданную работу и счётчик до дедлайна. В шапке — счётчики кандидатов, этапов, общая сумма баллов, средний этап, переключатели сортировки (по баллам / по этапу / А–Я) и админский тулбар (+ Кандидат, Выйти).
Та же доска в тёмной теме — переключение темы реализовано на фронте.
Матрица «материал × ученик». Колонки — теоретические материалы (статьи, доклады, гайды), строки — кандидаты. Ячейка показывает статус: пусто (не открыто) / прочитано (оранжевый чек) / зачтено с баллом (зелёный, например 5/10 или просто ✓). Внизу — суммарка n/N сколько человек коснулись материала, справа — общий прогресс кандидата (8/18 · 44%). Клик по ячейке — UPSERT оценки, клик по заголовку — детали материала.
Подробный профиль: ФИО, ник, город, возраст, био, цветная аватарка с инициалами, теги «сильные стороны» и «зоны роста», текущий этап и сумма баллов. Ниже — список всех этапов с маркерами состояния (done / current / todo), баллами, дедлайнами, ссылками на сданные работы и кнопкой готово · снять для перевода кандидата на следующий этап.
Базис проекта — шаблон vmkteam/gold-apisrv. Стек:
- Go 1.25, модуль
apisrv(бинарь тожеapisrv— название хранится вMakefile.mk). - PostgreSQL +
go-pg/v10(через форкvmkteam/pg/v10, прибитreplaceвgo.mod— не сносить при обновлениях). - JSON-RPC через
vmkteam/zenrpc— два независимых сервера на одномecho-инстансе. - mfd-generator — генерация моделей, репозиториев и search-структур БД из
docs/model/apisrv.mfd. - zenrpc (
go tool zenrpc) — генерация роутера и SMD из doc-комментариев в RPC-сервисах. - goconvey BDD-стиль для тестов; тесты бьют по живой PostgreSQL — никаких моков.
embedlogдля структурного логирования,appkitдля метаданных и pprof,sentry-goопционально.
Полный контракт API с полями и валидациями — в docs/RPC.md, исходная схема — в docs/apisrv.sql. Кратко, за что отвечает каждая сущность:
stages— справочник этапов курса, через которые последовательно проходят кандидаты.candidates— ученики; одновременно полноценные пользователи сервиса (свой логин/пароль/authKey, могут логиниться и редактировать профиль).candidateStages— прохождение конкретного этапа конкретным кандидатом: ссылка на сданную работу, оценка ментора, дедлайн, флаг «готово к проверке» и счётчик доработок. Одна строка покрывает и «попадание на этап», и «оценку за этап».materials— каталог теоретических материалов курса (книги, статьи, видео, тесты).candidateMaterials— прогресс кандидата по материалу: отметка «прочитано» от ученика и оценка от ментора. Создаётся лениво — при первой отметке или первой оценке.users— админы/менторы (самостоятельная регистрация отключена — заводятся сидом или вручную).statuses— справочник из трёх строк (enabled/disabled/deleted). Soft-delete по всему проекту — этоstatusId → 3.
candidate.advance(главная) — атомарно проставляет оценку текущему этапу, заводит запись для следующего и сдвигает указательcurrentStageId. На последнем этапе вместо этого ставитсяcompletedAt.candidate.rollback— возвращает кандидата на шаг назад, стирая последнюю оценку.candidate.setReady/material.setRead— кандидатские сигналы готовности к проверке.candidate.rate/material.score— админские оценки.
В pkg/rpc/middleware.go методы делятся на:
- open — без
Authorization2-заголовка (auth.login,auth.signUp,auth.register,*.get,*.getById,dashboard.summary,material.getProgress); если заголовок есть и валиден — principal подмешивается «оппортунистически». - registered — нужен любой валидный authKey, admin или candidate (
auth.me,candidate.setLink,candidate.setAvatarUrl,candidate.setReady,candidate.updateProfile,material.setRead,material.getMyProgress); кандидат может менять только свои объекты, админ — любые. - protected (всё остальное) — только admin. Кандидатский authKey возвращает
401.
docs/init.sql идемпотентен (re-run = no-op). Заводит справочник statuses, одного админа admin / пароль 12345, 15 этапов реального курса и 5 сидовых кандидатов (ivan.sokolov, maria.petrova, alex.ivanov, olga.novikova, dmitry.kuznetsov) с тем же паролем.
| Путь | Назначение |
|---|---|
/v1/rpc/ |
Публичный JSON-RPC (auth, candidate, stage, material, dashboard) |
/v1/rpc/doc/ |
SMDBox UI публичного API |
/v1/rpc/openrpc.json |
OpenRPC документ |
/v1/rpc/api.ts |
TypeScript-клиент публичного API (его и потребляет фронт) |
/v1/vt/ |
Админский JSON-RPC (auth, user) с Authorization2-заголовком |
/v1/vt/doc/ |
SMDBox UI VT |
/v1/vt/api.ts |
TypeScript-клиент VT (с классами) |
/status |
Healthcheck (пингует БД) |
/metrics |
Prometheus |
/debug/pprof/* |
pprof |
/debug/metadata |
appkit-метаданные |
/ |
Список роутов (только в IsDevel) |
Дефолтный порт — 8075.
Authorization2-заголовок — единый bearer-токен и для админа, и для кандидата. Middleware (pkg/rpc/middleware.go) делит методы на три уровня:
- open — без заголовка (например,
auth.login,auth.signUp,candidate.get,dashboard.summary); если заголовок есть и валиден, principal подмешивается «оппортунистически». - registered — нужен любой принципал (admin или candidate); per-row проверки внутри метода (
candidate.updateProfile,material.setRead, …). - protected (всё остальное) — только admin.
В StageScore.scored_by/updated_by пишется, кто проставил оценку.
docs/model/apisrv.mfd
│
├─ make mfd-model → pkg/db/{model,model_search,model_validate,model_params}.go
├─ make mfd-repo NS=<ns> → pkg/db/<ns>.go (репозитории)
└─ make mfd-db-test → pkg/db/test/
pkg/rpc/*.go (с doc-комментариями //zenrpc:...)
│
└─ make generate (go generate ./pkg/rpc)
→ pkg/rpc/rpc_zenrpc.go (роутер + SMD)
│
├─ make api-ts → docs/api.ts (TS-клиент для фронта)
└─ runtime: /v1/rpc/api.ts, /v1/rpc/openrpc.json
Хэнд-врайтенные расширения сгенерированных репозиториев лежат в pkg/db/apprentice_ext.go и pkg/db/apprentice_auth_ext.go — править их можно; сами apprentice.go, common.go, model*.go — нельзя (затрутся).
make test # полный цикл, требует $TEST_PGDATABASE
make test-short # отфильтровано регэкспом Test[^D][^B] — без БДКонвенция: тесты, которым нужна реальная БД, называются TestDB... или TestBatch... — тогда test-short их пропустит.
- Go 1.25.
- PostgreSQL (локальная или в Docker), доступ из консоли через
psql/createdb/dropdb. make.
make init # копирует Makefile.mk.dist → Makefile.mk и cfg/local.toml.dist → cfg/local.toml
make tools # ставит mfd-generator, pgmigrator, colgen, golangci-lint v2.8.0После make init отредактируйте:
Makefile.mk— имя БД, креды Postgres:PGDATABASE ?= apprentice PGHOST ?= localhost PGPORT ?= 5432 PGUSER ?= postgres PGPASSWORD ?= postgres TEST_PGDATABASE ?= test-apprentice
cfg/local.toml— те же значения в секции[Database]:[Database] Addr = "localhost:5432" User = "postgres" Password = "" Database = "apprentice"
make db делает всё за один проход — пересоздаёт БД и накатывает схему + сид:
make db
# эквивалент:
# dropdb --if-exists -f apprentice
# createdb apprentice
# psql -f docs/apisrv.sql apprentice # схема
# psql -f docs/init.sql apprentice # справочники + сидовые этапы/кандидатыПосле этого есть готовый админ admin / пароль 12345 и пять сидовых кандидатов (тоже 12345).
Тестовая БД накатывается аналогично:
make db-test # пересоздаёт $TEST_PGDATABASEmake run # go run ./cmd/apisrv -config=cfg/local.toml -devСервис поднимется на http://localhost:8075. Полезные точки входа:
http://localhost:8075/v1/rpc/doc/— интерактивный SMDBox по публичному API.http://localhost:8075/v1/vt/doc/— то же для VT (админ).http://localhost:8075/status— healthcheck.http://localhost:8075/— список всех роутов (только в-dev).
- Накатить миграцию на локальную БД (или
make dbдля полного пересоздания). make mfd-xml— обновитьdocs/model/apprentice.xmlс реальной схемы.make mfd-model— перегенерироватьpkg/db/model*.go.make mfd-repo NS=apprentice— перегенерироватьpkg/db/apprentice.go(дляNS=common—common.go).- Обновить
docs/apprentice.pgd(pgDesigner) иdocs/RPC.md, если поменялись публичные структуры.
- Меняем код в
pkg/rpc/<service>.go(doc-комментарии//zenrpc:...— без бэктиков, иначеmake generateругнётся непонятным AST-эррором). make generate— перегенерируетсяpkg/rpc/rpc_zenrpc.go.make api-ts— обновитсяdocs/api.tsдля фронта.make fmt lint test— обязательный финальный прогон.
make fmt lint testВсе три должны быть зелёные. Если тестовая БД пустая или схема устарела — сначала make db-test.



