Перейти к содержимому

Сиды (наполнение БД)

Каталог сидов: apps/core-api/prisma/seeds.

ФайлЧто делает
init.seed.tsточка входа: порядок запуска и границы транзакций
platform-role.seed.tsPlatformRoleSeeder — пять строк roles по детерминированным id
geo.seed.tsGeoSeeder — гео-справочник: таймзоны, страны, локации, связки
geo-data.transformer.tsзагрузка и нормализация данных RestCountries (без обращения к БД)
dev-fixtures.seed.tsDevFixturesSeeder — пользователи, команда и проекты для разработки

Сиды — не «тестовые данные для удобства»: без PlatformRoleSeeder на чистой базе нельзя создать ни одного пользователя, включая платформенных, потому что строки roles перестали заводиться на лету при регистрации.

Окно терминала
nx run core-api:prisma:seed:run

Под капотом — ts-node -r tsconfig-paths/register --project tsconfig.app.json prisma/seeds/init.seed.ts (рабочая директория apps/core-api). Таргет поддерживает конфигурации development, stage и production с соответствующими env-файлами.

Все сиды идемпотентны: работают через upsert по детерминированным ключам, повторный прогон не плодит дублей.

  1. PlatformRoleSeeder — своя транзакция, идёт первым.
  2. GeoSeeder — отдельная транзакция, обёрнута в try/catch.
  3. DevFixturesSeeder — только при APP_ENV ∈ {development, stage} (по умолчанию development).

Транзакции разделены намеренно: GeoSeeder ходит во внешний API, и его сбой не имеет права забрать с собой роли. Параметры транзакций — { maxWait: 5000, timeout: 300000 }; большой таймаут нужен из-за объёма гео-данных.

Внешний источник справочника стран (restcountries v3.1) объявлен устаревшим и отдаёт заглушку вместо массива, поэтому GeoSeeder сейчас падает на любой машине. Ошибка печатается и не роняет прогон — роли и dev-фикстуры создаются. Практическое следствие: после сидирования справочники countries / locations / timezones пусты, а Profile.timezoneId и Profile.locationId заполнить нечем. URL источника переопределяется переменной RESTCOUNTRIES_URL.

Заводит пять строк roles по фиксированным id: CUSTOMER, EMPLOYEE, ADMIN, OPERATOR, ARBITER. Id берутся из констант, а не генерируются:

РольИсточник id
CUSTOMER, EMPLOYEECUSTOMER_ROLE, EMPLOYEE_ROLEapis/providers/user-api/data-access
ADMIN, OPERATOR, ARBITERADMIN_ROLE_ID, OPERATOR_ROLE_ID, ARBITER_ROLE_IDapis/shared

Три платформенных id продублированы ещё и в предикате частичного индекса users_on_roles_single_platform_role (см. migrations). Менять их можно только вместе с миграцией, пересоздающей индекс.

Наполняет timezones, countries, locations, countries_timezones, locations_timezones из RestCountries. Порядок работы:

  1. fetchRestCountries() — HTTP-загрузка с retry (3 попытки, пауза 1 с); URL — из RESTCOUNTRIES_URL, иначе https://restcountries.com/v3.1/all?fields=cca2,cca3,name,timezones. Ответ обязан быть массивом, иначе ошибка.
  2. transformToGeoData(raw) — чистая нормализация в структуру GeoData: набор таймзон (со смещением, разобранным из строк вида UTC+01:00), плоские записи стран, дерево локаций REGION → SUBREGION → COUNTRY → CITY и списки связок.
  3. upsertTimezonesupsertCountries (по cca2) → upsertLocationsupsertCountryTimezonesupsertLocationTimezones. Локации создаются по возрастанию уровня, чтобы родитель существовал раньше потомка; из-за NULL в parent_id вместо upsert используется пара findFirst + update/create.

Шаги связаны строковыми ключами, которые сидер превращает в реальные UUID.

Опорный набор данных, из которого можно войти каждой ролью и увидеть проект в двух состояниях. Все id детерминированы (d0000000-…, d1000000-…, d2000000-…).

E-mailРольВход
admin@crewsforge.comADMINодноразовый код admin-api; пароля нет (passwordHash = null)
operator@crewsforge.comOPERATORто же
arbiter@crewsforge.comARBITERто же

Домен crewsforge.com обязан входить в ADMIN_ALLOWED_DOMAINS — иначе admin-api отвергнет и регистрацию, и вход по allowlist’у домена. Значение по умолчанию совпадает с dokploy/.env.example. Каждому пользователю выдаётся ровно одна платформенная роль: вторая физически отвергается частичным уникальным индексом БД.

Домен example.com — намеренно не платформенный: в админку эти учётки попадать не должны. Пароль у всех один — DevPassword123!; пишется только при создании (соль bcrypt случайна, перезапись хеша на каждом прогоне ломала бы идемпотентность строки).

E-mailКтоЧленство в команде
founder@example.comзаказчик (Customer + CustomerProfile)
team.lead@example.comсотрудник, 96 мес. опыта, FULL_TIMErole = ADMIN, canSign = true, canSubmit = false
team.engineer@example.comсотрудник, 48 мес. опыта, CONTRACTrole = VIEWER, canSign = false, canSubmit = true

Двое участников команды разведены по осям намеренно: role — это уровень доступа, canSign / canSubmit — полномочия, и фикстура показывает, что они независимы. Команда — Forge Crew (slug forge-crew, статус ACTIVE, проставлен verifiedAt).

ПроектСтатусЧем полезен
Internal knowledge baseDRAFTчерновик: описание есть, бюджет и категория не заданы
Marketplace payouts revampTEAM_MATCHINGопорная точка мэтчинга: категория WEB_DEVELOPMENT, валюта USD, вилка бюджета $20 000 – $35 000 в минорных единицах, teamId ещё пуст
  • Засевается: роли платформы (всегда), гео-справочник (пока внешний источник отдаёт данные), а на development/stage — шесть пользователей, команда и два проекта.
  • Не засевается: навыки, специализации, языки, спецификации, планы, этапы, деньги, задачи очереди — эти таблицы наполняет только код сервисов.

Применимость сидов на живой БД проверяется интеграционным тестом apps/core-api-e2e/src/schema/seed-data.integration.spec.ts.