架构¶
平台遵循整洁架构(Clean Architecture):handler → usecase → repository →
domain。业务核心从不 import Web 框架。
组成部分¶
Internet ──► nginx (TLS)
│
├─ /oauth2/*, /.well-known/*, /userinfo ─► Go API — 内置 OIDC issuer
├─ /rp/sign/* ─► eID 签署中继(后端)
├─ /rp/eid/* ─► eID 服务代理 — 个人(后端)
├─ /rp/eid-org/* ─► eID 服务代理 — 组织(后端)
└─ 其余全部 ─► Next.js BFF (web) ──► 后端 API (:8080)
│
内部网络: db (PostgreSQL) · redis
分层¶
| 层 | 技术 | 说明 |
|---|---|---|
| 后端 | Go · chi (net/http) · pgx(无 ORM) | 整洁架构、RLS、手写 SQL |
| 前端 | Next.js 16 (BFF) + @gerege/ui-core |
浏览器只与同源路由通信;令牌绝不进入客户端 JS |
| OIDC 提供方 | 内置(Go,usecases/oidc) | 平台自行驱动登录/授权/登出流程 |
| 身份 | eID Mongolia RP | 电子身份证验证 |
| 缓存/队列 | Redis | 会话拒绝名单、临时状态 |
| AI | Gemini(免 SDK 的 REST) | 聊天、语音、翻译 |
安全¶
- 行级安全(RLS) — 每个用户只能看到属于自己的数据行;启动时会有可执行性守卫 (生产环境要求使用非超级用户角色)。
- BFF 模式 — 令牌保存在 httpOnly Cookie 中,绝不出现在浏览器 JS 里。
- 双重 CSRF — 自定义请求头 + 来源(origin)校验。
- 安全响应头 — CSP、HSTS、COOP/COEP/CORP;按 IP 限流。
- 审计 — 哈希链式、仅追加的审计轨迹。
后端目录结构 —— 来自内核¶
上述所有能力(认证、RBAC、网关、审计、OIDC 提供方、eID/SSO、AI)都没有写在
本仓库里 —— 它们通过 go.mod 来自 open-gerege-core 这个 Go 模块。因此
backend/ 中只有一个 Go 文件:
backend/
├── cmd/api/main.go # 约 30 行:启动内核,再加上自己的路由
├── deploy/ # Dockerfile、数据库初始化
└── .env.example # 配置模板
func main() {
server.ServiceName = "gerege-template"
app, err := server.NewApp() // ← 全部能力来自内核
// 在此添加本应用自己的路由:
// app.Router().Route("/api/xxx", xxx.Routes(app.Pool()))
app.Run()
}
内核分为两层:open-gerege-core(开放基座,政务线直接使用)与通过
go.mod 继承它的 private-gerege-core(封闭,供 Gerege 线使用)。
前端目录结构 — @gerege/ui-core¶
前端的大部分代码位于共享包中。应用只保留自身特有的部分:品牌、landing 文案 以及平台特定页面。
frontend/
├── src/brand.config.ts # 品牌信息的唯一来源(名称、域名、docsUrl…)
├── src/components/landing/ # 应用自己的 landing 文案
├── src/app/api/**/route.ts # BFF 路由的单行包装(共 158 个)
└── node_modules/@gerege/ui-core
├── src/api/** # BFF 路由的真正逻辑
└── src/components/** # AppShell、UserMenu、admin/gov/gateway 页面
- 该包在
package.json中按 tag 固定 (…/ui-core/archive/refs/tags/v0.4.0.tar.gz)— 升级是显式的,不会误升。 - 每个路由都是包装:
export { GET, POST } from '@gerege/ui-core/api/<路径>'。 Next.js 依据文件系统注册路由,因此包装不可省略;npm run check:routes会抓出包内存在、但应用中没有包装的路由。 - 该包绝不 import 应用的
brand.config.ts— 相关值通过<UiCoreProvider>传入(brandName、docsUrl、docsLangs)。在其他文件里硬编码品牌名会被npm run check:brand拒绝。
两项检查都属于 npm run build,因此由 CI 强制执行。