Хөгжүүлэгчийн гарын авлага
Локал ажиллуулахаас эхлээд модуль, UI, өөрийн дистрибуц, registry, гадны системтэй OIDC-ээр холбох, цөмд хувь нэмэр оруулах хүртэл — нэг хуудсанд.
Модуль бол pkg/nexus.Module interface-ийг хэрэгжүүлсэн Go package. Tenant тусгаарлалт, нэвтрэлт, суулгалт, RBAC оноолт, audit — платформ хийнэ; та бизнес логикоо л бичнэ. Хамгийн сайн заавар бол ажиллаж байгаа жишээ — apps/devices (нэг resource), apps/organisation (олон resource, олон хуудас).
0. Эхлэх — локал ажиллуулах
Бүх команд Makefile-аар (make help). Push-ийн өмнө make check — linux build + vet + test + SDK-ийн хил. Хөгжүүлэлтийн төрлүүд:
- Модуль — бизнес функц (энэ репогийн apps/ эсвэл өөрийн репо); доорх §1–8
- Дистрибуц — өөрийн компанид nexus-mini ажиллуулах, модулиуд сонгох (nexus CLI)
- Интеграц — гадны систем nexus-mini-ээр нэвтрэх / nexus-mini Google, SSO-оор нэвтрэх (OIDC)
- Цөм — backend/internal, frontend, admin — PR-аар (доод хэсэг)
Хэн юу хариуцдаг вэ
| МОДУЛЬ | ПЛАТФОРМ |
|---|---|
| Permission-оо тунхаглана | Tenant тусгаарлалт (RLS) |
| Цэсээ зарлана | Нэвтрэлт, session |
| Route-уудаа бүртгэнэ | Суулгалт, хамаарлын шийдэл |
| Өөрийн хүснэгт, миграц | RBAC default оноолт, шалгалт |
| Бизнес логик | Audit гинж, app store |
| Хувилбар + манифест (make manifest) | Registry, гарын үсэг, nexus add/upgrade |
| — | Түдгэлзүүлэлт / зөвхөн-унших, impersonation, lockout |
Файлын бүтэц
Жижиг модуль нэг файлаас эхэлж болно; өсөхөөрөө ингэж хуваана:
Олон resource-тэй бол handler файлыг resource тус бүрээр салгана (organisation: departments.go, people.go; ui/pages/departments/, ui/pages/people/).
Алхамууд
1. Package үүсгэх
Version нь semver; registry-д нийтлэх git tag нь v<Version>. Permission нэмсэн/өргөсгөсөн бол minor-оо өсгө — дистрибуц nexus upgrade хийхэд -approve асуух шалтгаан нь энэ.
2. Permission тунхаглах
Дүрмүүд (зөрчвөл бинари асахгүй):
- Код заавал
<ShortID>.-ээр эхэлнэ — өөр модулийн эрхийг булааж чадахгүй DefaultRolesнь суулгах үед хэн авахыг тунхагладаг:adminүргэлж бүгдийг авна, жагсаалтад бичсэн нь нэмж авна,"user:own"нь зөвхөн өөрийн мөрийн эрхDefaultRolesхоосон = зөвхөн admin (аюулгүй default)"role:own"бичихийн тулд permissionOwnScope: trueбайх ёстой (үгүй бол panic). Runtime-д ч own_scope=false permission-д хэн ч «own» өгч чадахгүй- Нөөцөлсөн ShortID:
core api admin platform store apps developers login signup dashboard members roles audit settings org - Шинэ хувилбарт permission нэмбэл цөм асахдаа суусан tenant бүрийн admin-д (+DefaultRoles) автоматаар оноодог (backfill); байгаа кодод хүрэхгүй
3. Миграц
tenant_id uuid NOT NULL+ RLS policy (app_tenant_id()) — жишээг devices-ээс хуулOwnScopeашиглах болcreated_by uuidбагана заавал- Бүх string баганад урттай хязгаар (varchar(n)) — задгай text хориотой
- Төгсгөлд нь
GRANT ... ON <table> TO nexus_app, nexus_admin(функцэд автомат GRANT байхгүй) - Өөр хүснэгт рүү FK (memberships, өөрийн мод) заавал same-tenant trigger-тэй — FK шалгалт RLS-ийг давдаг. Загвар:
apps/organisation/migrations/00002_same_tenant.sql - Апп role-д temp хүснэгт, users.password_hash, auth_* функцууд хаалттай — зориуд
Модуль бүр өөрийн goose хүснэгттэй (goose_<shortid>) тул цөм болон бусад модультай мөргөлдөхгүй.
4. Route-ууд
Танд өгөгдөх r нь аль хэдийн хамгаалагдсан: /api/apps/<ShortID>/ дор байрладаг, нэвтрээгүй хүн 401, апп суулгаагүй tenant 403 авчихсан байдаг. Handler дотор:
nexus.TenantID(ctx),nexus.UserID(ctx)— хүсэлтийн identitynexus.Scope(ctx)—ScopeOwnбол query-дээcreated_byшүүлт нэмdeps.DB— RLS context автоматаар тохирдог холболт; SQL-дааtenant_id = $1гэж бас бичdeps.Audit.Record(ctx, ...)— чухал үйлдлээ audit гинжид бичnexus.JSON / Decode / Error / DBError— вэб туслахуудnexus.UUIDParam(w, r, "id")/nexus.IsUUID— зам/биеийн id-г DB-д хүргэхээс өмнө (буруу бол 400); string талбарын уртыг valid()-даа шалга- Түдгэлзүүлсэн байгууллага → 403, зөвхөн-унших → бичих 503: платформ RequireTenant-д хийнэ, модуль мэдэх шаардлагагүй; impersonated session-ийн audit-д impersonated_by автоматаар хавсарна
5. Цэс
Path заавал /<ShortID> эсвэл /<ShortID>/… — өөр зам Register panic (portal-ийн middleware нийтийн замаас бусдыг хамгаалдаг, модуль тойрч чадахгүй). Icon нэр: components/icons.tsx.
6. UI хуудас (portal)
UI нь модулийн хавтаст амьдарна: ui/pages/ доторх хуудсууд build үед app/(portal)/<нэр>/ руу хуулагдана, ui/i18n.ts толь цөмийнхтэй нэгдэнэ — цөмийн frontend файлд гар хүрэхгүй. Бэлэн загвар (apps/devices/ui/pages/page.tsx, apps/organisation/ui).
- Эрхээр UI-гаа нуу:
me.permissions["name.manage"]байхгүй бол товчоо бүү харуул (энэ нь UX — жинхэнэ хамгаалалт серверт) - «Өөрийн» scope-той хэрэглэгчид засах/устгах товчийг
created_by === me.user.idүед л харуулна - Цэсний icon нэрээ
components/icons.tsx-ийн map-д нэм (lucide icon) - Бэлэн загварууд:
card / table / btn / field / badge / modal— globals.css; амжилтадtoast(...), текстэдt(...) - Толь:
ui/i18n.ts—{ en: { "Төхөөрөмжүүд": "Devices" } }(түлхүүр нь монгол текст)
7. Бүртгэх ба асаах
8. Store-д нийтлэх
Манифест кодоос үүснэ: make manifest MOD=name > manifests/name.json — дараа нь nexus-registry репод PR илгээнэ; maintainer index.json-ийг Ed25519-ээр гарын үсэглэнэ. Код registry-д хадгалагдахгүй — go_module зам + git tag хангалттай. Орсны дараа хэн ч nexus add name гэж дистрибуцдаа нэмнэ. Өөрийн registry: репог хуулж, nexus-registry keygen → REGISTRY_URL + REGISTRY_KEYS.
Өөрийн дистрибуц — цөмийг fork хийхгүй
Өөрийн компанид nexus-mini ашиглаж, өөрийн модулиуд, өөрийн store, өөрийн харилцагчидтай (tenant) платформ ажиллуулж болно. Цөмийн репог хуулбарлаж засахгүй — хамаарал болгоно. Ингэж байж цөмийн шинэчлэлтийг merge-гүй, мөргөлдөөнгүй авна.
core.Main— migrate/serve/manifest коммандууд, env, миграц, анхны админ, permission sync, сервер: бүгд цөмд; та модулиудаа л өгнөnexus upgrade— модулийн шинэ хувилбарт permission нэмэгдсэн/өргөссөн бол зогсоож -approve шаардана; модуль чимээгүй эрх авахгүй- Frontend: цөмийн frontend-ийн хуулбар +
modules.json. Та цөмийн файлд гар хүрдэггүй (UI ui/-д, толь ui/i18n.ts-д) тул цөмийн frontend-ийг tag-аас хуулж дарахад мөргөлдөхгүй - SDK амлалт:
pkg/nexus+core.Mainv1.x дотор эвдэхгүй (semver); internal/* чөлөөтэй өөрчлөгдөнө — модуль түүнээс импортолж чадахгүй - Цөмд алдаа олбол өөр дээрээ засахгүй — upstream руу PR. Харилцагч тань таны instance дээр tenant болно; та платформ админ
Гадны систем холбох — OIDC provider, SSO, federation
nexus-mini нь OpenID Connect provider (таны систем энэ платформын бүртгэлээр нэвтэрнэ) ба relying party (энэ платформ Google/өөр issuer-ээр нэвтэрнэ) хоёулаа. Хоёр nexus-mini хоорондоо = federation.
Таны систем nexus-mini-ээр нэвтрэх
- Portal → SSO клиентүүд (core.sso.manage) → Клиент нэмэх: нэр, redirect URI (https/localhost), scope. Confidential бол client_secret нэг л удаа харагдана; SPA/mobile бол Public (PKCE).
- Discovery:
<PORTAL_URL>/api/oauth2/.well-known/openid-configuration— дурын OIDC номын сан (openid-client, oidc-client-ts, Spring, NextAuth…) үүгээр бүх endpoint-ийг олно. - Урсгал: authorization_code + PKCE S256 заавал. Хэрэглэгч portal-д нэвтэрч, клиентийн байгууллагын гишүүн бол consent → code → /token. Зөвшөөрөл санагдана.
- Токен: access opaque (/introspect, /revoke), id_token RS256 (/jwks), offline_access → refresh (rotation, replay бол гэр бүлээр хүчингүй). Claims: sub, name, email, tenant (slug) + tenant_id, roles.
- Сервер-сервер: client_credentials → tenant scope-той access token. Гарах: end_session?id_token_hint=…
nexus-mini Google / өөр OIDC-ээр нэвтрэх (env)
Login хуудсанд товч гарна; PKCE + state + nonce, id_token-ийг issuer-ийн JWKS-ээр шалгана. Дэлгэрэнгүй: docs/04-integrations.md.
Аюулгүй байдлын дүрэм — модуль, цөм хоёуланд
- Эрх зөвхөн серверт: route бүр RequirePermission; UI-гийн нуулт нь UX. Мөрийн түвшин — RLS + query-дээ tenant_id, own scope бол created_by.
- SQL үргэлж параметртэй, төрлийн cast-тай ($1::uuid). Бүх string багана varchar(n); FK бусад хүснэгт рүү → same-tenant trigger.
- Алдааг клиентэд түүхийгээр нь буцаахгүй (DBError/Error): 23505 → 409, бусад → лог + ерөнхий 500. Буруу uuid → 400 (UUIDParam).
- Нууц зүйл (токен, нууц үг, session) лог/audit-д бичихгүй. Client secret argon2-оор hash-лагдсан; OAuth токен sha256 hash-аар хадгалагдана.
- Апп DB role-д: temp хүснэгт үгүй, auth_* функц үгүй, users.password_hash үгүй, tenants төлөв багана үгүй — модуль эдгээрт хүрч чадахгүй, хүрэх гэж бүү оролд.
- Cookie-тэй бичих хүсэлт Origin + Sec-Fetch-Site шалгалттай; гадны домэйноос дуудах endpoint бол токен-аар танигддаг байх (OAuth2 загвар).
- Render дотор window/localStorage/matchMedia уншихгүй (hydration) — useEffect-д. Шалгахдаа dark/light хоёуланг.
Цөмд хувь нэмэр оруулах
- Бүтэц:
backend/internal/core/*(цөмийн дотоод — чөлөөтэй өөрчлөгдөнө),backend/pkg/nexus+backend/core.Main(SDK — v1.x-д эвдэхгүй, эвдэх бол major),backend/pkg/registry(registry гэрээ),frontend/,admin/. - Миграц: backend/db/migrations/000NN_*.sql (goose, Up/Down хоёулаа); definer функц бүр SET search_path = pg_catalog, public, pg_temp + REVOKE FROM PUBLIC + тодорхой GRANT.
- Тест: unit (go test) + env-гэйт integration (NEXUS_TEST_DATABASE_URL*); RLS/RBAC өөрчлөлтөд integration заавал. make check push бүрийн өмнө.
- Баримт: docs/00-decisions.md-д шийдвэрээ, docs/03 ↔ энэ хуудас синк, CLAUDE.md-ийн invariant-ууд.
- PR: жижиг, нэг зорилготой; commit-д «яагаад». Алдаа олбол fork-доо биш upstream-д.
Тест · Deploy
SQL parse/encode бүх логикт unit тест бич. make check нь linux cross-build + vet + test + SDK-ийн хилийн шалгалт (модуль internal/* импортолбол унадаг) — push бүрийн өмнө заавал. Deploy: сервер дээр git pull → deploy/deploy.sh (make migrate ENV_FILE=… + атом бинари/Next солилт + systemd restart); unit/nginx өөрчлөлт гараар. Docker: docker-compose.yml. Production: ENVIRONMENT=production, PORTAL_URL https, nexus_auth role, ADMIN_* env-ээс анхны админ үүссэний дараа устга.
Бэлэн үү?
devices-ийг хуулж эхлээд, дуусаад каталогт PR илгээгээрэй — эсвэл өөрийн дистрибуц үүсгээрэй.