Хөгжүүлэгчийн гарын авлага

Локал ажиллуулахаас эхлээд модуль, UI, өөрийн дистрибуц, registry, гадны системтэй OIDC-ээр холбох, цөмд хувь нэмэр оруулах хүртэл — нэг хуудсанд.

Модуль бол pkg/nexus.Module interface-ийг хэрэгжүүлсэн Go package. Tenant тусгаарлалт, нэвтрэлт, суулгалт, RBAC оноолт, audit — платформ хийнэ; та бизнес логикоо л бичнэ. Хамгийн сайн заавар бол ажиллаж байгаа жишээ — apps/devices (нэг resource), apps/organisation (олон resource, олон хуудас).

0. Эхлэх — локал ажиллуулах

$ git clone https://github.com/gerege-systems/nexus-mini && cd nexus-mini
$ psql -v owner_pw=… -v app_pw=… -v admin_pw=… -v auth_pw=… -f deploy/01-roles.sql  # нэг удаа, superuser
$ cp .env.example backend/nexus-mini.env  # 4 DB URL + ADMIN_EMAIL/NAME/PASSWORD + PORTAL_URL
$ make migrate && make serve  # API :8084, анхны платформ админ env-ээс
$ cd frontend && pnpm install && cd .. && make web  # portal :3020; make admin — :3021
$ docker compose up -d  # эсвэл бүгд нэг коммандаар

Бүх команд Makefile-аар (make help). Push-ийн өмнө make check — linux build + vet + test + SDK-ийн хил. Хөгжүүлэлтийн төрлүүд:

Хэн юу хариуцдаг вэ

МОДУЛЬПЛАТФОРМ
Permission-оо тунхагланаTenant тусгаарлалт (RLS)
Цэсээ зарланаНэвтрэлт, session
Route-уудаа бүртгэнэСуулгалт, хамаарлын шийдэл
Өөрийн хүснэгт, миграцRBAC default оноолт, шалгалт
Бизнес логикAudit гинж, app store
Хувилбар + манифест (make manifest)Registry, гарын үсэг, nexus add/upgrade
Түдгэлзүүлэлт / зөвхөн-унших, impersonation, lockout

Файлын бүтэц

Жижиг модуль нэг файлаас эхэлж болно; өсөхөөрөө ингэж хуваана:

backend/apps/<name>/
  module.go         модулийн ГЭРЭЭ: ID, permission, цэс, миграц, route↔permission холболт
  types.go          хүсэлт/хариултын struct + validation
  handlers.go       HTTP handler-ууд (нэг resource = нэг файл)
  migrations/       модулийн goose миграцууд
  ui/pages/         portal хуудсууд → build үед app/(portal)/<нэр>/ руу хуулагдана
  ui/i18n.ts        модулийн толь (en: {...}) — цөмийн толинд нэгдэнэ

Олон resource-тэй бол handler файлыг resource тус бүрээр салгана (organisation: departments.go, people.go; ui/pages/departments/, ui/pages/people/).

Алхамууд

1. Package үүсгэх

func (m *Module) ID() string { return "mn.yourorg.name" } // reverse-DNS, глобал давтагдашгүй
func (m *Module) ShortID() string { return "name" } // permission prefix + URL зам
func (m *Module) Name() string { return "Хүний нэр" }
func (m *Module) Version() string { return "1.0.0" }
 
// заавал биш — store/registry-ийн тайлбар кодоос (make manifest авдаг):
func (m *Module) Description() string { return "Юу хийдэг вэ…" }
func (m *Module) Publisher() string { return "your-org" }

Version нь semver; registry-д нийтлэх git tag нь v<Version>. Permission нэмсэн/өргөсгөсөн бол minor-оо өсгө — дистрибуц nexus upgrade хийхэд -approve асуух шалтгаан нь энэ.

2. Permission тунхаглах

func (m *Module) Permissions() []nexus.PermissionDefinition {
  return []nexus.PermissionDefinition{
    {Code: "name.read", DefaultRoles: []string{"manager", "user"}},
    {Code: "name.manage", OwnScope: true, DefaultRoles: []string{"manager", "user:own"}},
  }
}

Дүрмүүд (зөрчвөл бинари асахгүй):

3. Миграц

//go:embed migrations/*.sql
var migrations embed.FS
func (m *Module) Migrations() fs.FS { return migrations }

Модуль бүр өөрийн goose хүснэгттэй (goose_<shortid>) тул цөм болон бусад модультай мөргөлдөхгүй.

4. Route-ууд

func (m *Module) RegisterRoutes(r chi.Router, deps nexus.Deps) {
  h := &handler{deps: deps}
  r.With(nexus.RequirePermission(deps.Perms, "name.read")).Get("/", h.list)
  r.With(nexus.RequirePermission(deps.Perms, "name.manage")).Post("/", h.create)
}

Танд өгөгдөх r нь аль хэдийн хамгаалагдсан: /api/apps/<ShortID>/ дор байрладаг, нэвтрээгүй хүн 401, апп суулгаагүй tenant 403 авчихсан байдаг. Handler дотор:

5. Цэс

func (m *Module) Menus() []nexus.MenuDefinition {
  return []nexus.MenuDefinition{{
    ID: "name.list", Label: "Монгол нэр", Labels: map[string]string{"en": "English"},
    Path: "/name", Icon: "device", Order: 10,
  }}
}

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).

// apps/name/ui/pages/page.tsx → /name (ui/pages/reports/page.tsx → /name/reports)
"use client";
 
export default function NamePage() {
  const { me } = useShell();  // хэрэглэгч + permissions
  const { t } = useT();      // хэл (mn/en)
  const manage = me.permissions["name.manage"]; // undefined | "all" | "own"
  // api.get(`/api/apps/name/`) — cookie автоматаар, 401 бол login руу
}

7. Бүртгэх ба асаах

// backend/apps/apps.go — бинарид орох модулиуд:
func All() []nexus.Module { return []nexus.Module{ devices.New(), name.New() } }
 
// frontend/modules.json — portal-д орох UI:
{ "short_id": "name", "ui": "../backend/apps/name/ui" }
 
$ make migrate && make serve  # модуль store-д гарч ирнэ

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-гүй, мөргөлдөөнгүй авна.

// nexus CLI — дистрибуц үүсгэх, модуль нэмэх (цөмийг fork хийхгүй):
$ go run github.com/gerege-systems/nexus-mini/backend/cmd/nexus@latest init my-dist
$ cd my-dist && go run github.com/gerege-systems/nexus-mini/backend/cmd/nexus@latest add organisation
$ make migrate && make serve
 
// backend/main.go (init үүсгэнэ; add маркер хооронд мөр нэмнэ):
func main() { core.Main(modules()...) }
 
// Цөмийг шинэчлэх — merge байхгүй, зөвхөн хувилбар:
$ go get github.com/gerege-systems/nexus-mini/backend@v1.5.0
$ git fetch upstream --tags && git checkout backend/v1.5.0 -- frontend

Гадны систем холбох — OIDC provider, SSO, federation

nexus-mini нь OpenID Connect provider (таны систем энэ платформын бүртгэлээр нэвтэрнэ) ба relying party (энэ платформ Google/өөр issuer-ээр нэвтэрнэ) хоёулаа. Хоёр nexus-mini хоорондоо = federation.

Таны систем nexus-mini-ээр нэвтрэх

  1. Portal → SSO клиентүүд (core.sso.manage) → Клиент нэмэх: нэр, redirect URI (https/localhost), scope. Confidential бол client_secret нэг л удаа харагдана; SPA/mobile бол Public (PKCE).
  2. Discovery: <PORTAL_URL>/api/oauth2/.well-known/openid-configurationдурын OIDC номын сан (openid-client, oidc-client-ts, Spring, NextAuth…) үүгээр бүх endpoint-ийг олно.
  3. Урсгал: authorization_code + PKCE S256 заавал. Хэрэглэгч portal-д нэвтэрч, клиентийн байгууллагын гишүүн бол consent → code → /token. Зөвшөөрөл санагдана.
  4. Токен: access opaque (/introspect, /revoke), id_token RS256 (/jwks), offline_access → refresh (rotation, replay бол гэр бүлээр хүчингүй). Claims: sub, name, email, tenant (slug) + tenant_id, roles.
  5. Сервер-сервер: client_credentials → tenant scope-той access token. Гарах: end_session?id_token_hint=…
// 1. browser → authorize (PKCE):
<issuer>/authorize?response_type=code&client_id=…&redirect_uri=…&scope=openid%20profile%20email&state=…&nonce=…&code_challenge=…&code_challenge_method=S256
// 2. callback-ийн code-оор токен:
$ curl -u "$CLIENT_ID:$CLIENT_SECRET" -X POST <issuer>/token -d grant_type=authorization_code -d code=… -d redirect_uri=… -d code_verifier=…
// 3. шалгах / хүчингүй болгох:
$ curl -u … -X POST <issuer>/introspect -d token=…   $ curl -u … -X POST <issuer>/revoke -d token=…

nexus-mini Google / өөр OIDC-ээр нэвтрэх (env)

GOOGLE_CLIENT_ID=… GOOGLE_CLIENT_SECRET=…  # redirect: <PORTAL_URL>/api/auth/sso/google/callback
SSO_ISSUER=https://nexus.bold.mn/api/oauth2 SSO_CLIENT_ID=… SSO_CLIENT_SECRET=… SSO_NAME="Bold SSO"  # federation
SSO_AUTO_SIGNUP=false  # true: танигдаагүй имэйлд данс үүсгэнэ (JIT)

Login хуудсанд товч гарна; PKCE + state + nonce, id_token-ийг issuer-ийн JWKS-ээр шалгана. Дэлгэрэнгүй: docs/04-integrations.md.

Аюулгүй байдлын дүрэм — модуль, цөм хоёуланд

Цөмд хувь нэмэр оруулах

Тест · 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 илгээгээрэй — эсвэл өөрийн дистрибуц үүсгээрэй.
Markdown хувилбарАпп дэлгүүр үзэх