Route all feature requests through business API modules, sync generated DTOs, and add communication tests and architecture documentation. BREAKING CHANGE: migrate the client to plural resource paths, canonical snake_case fields, ISO timestamps, and explicit paginated responses.
159 lines
12 KiB
XML
159 lines
12 KiB
XML
<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1420" viewBox="0 0 1800 1420" role="img" aria-labelledby="title desc">
|
|
<title id="title">OpenCloud 当前架构</title>
|
|
<desc id="desc">前端 Vue 页面和 Pinia store 经业务 API 模块、HTTP 传输层访问 Hono 后端;后端路由使用校验、鉴权、Drizzle 和复用服务,集中序列化响应。Better Auth 独立处理认证,认证与业务共用 PostgreSQL,图片存于对象存储,邮件由 Resend 发送。下方展示从后端 Zod schema 生成并同步前端 TypeScript 契约的开发流程。</desc>
|
|
<defs>
|
|
<marker id="arrow" markerWidth="9" markerHeight="9" refX="8" refY="4.5" orient="auto-start-reverse"><path d="M0 0 9 4.5 0 9" fill="#34745d"/></marker>
|
|
<marker id="return" markerWidth="9" markerHeight="9" refX="8" refY="4.5" orient="auto"><path d="M0 0 9 4.5 0 9" fill="#0284c7"/></marker>
|
|
<marker id="build" markerWidth="9" markerHeight="9" refX="8" refY="4.5" orient="auto"><path d="M0 0 9 4.5 0 9" fill="#a16207"/></marker>
|
|
<style>
|
|
text { font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif; fill: #243746; }
|
|
.title { font-size: 42px; font-weight: 750; letter-spacing: -1px; }
|
|
.section { font-size: 26px; font-weight: 700; }
|
|
.node { font-size: 23px; font-weight: 700; }
|
|
.body { font-size: 19px; }
|
|
.small { font-size: 16px; }
|
|
.muted { fill: #64748b; }
|
|
.code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace; font-size: 16px; fill: #475569; }
|
|
.flow { fill: none; stroke: #34745d; stroke-width: 2.5; marker-end: url(#arrow); stroke-linejoin: round; }
|
|
.response { fill: none; stroke: #0284c7; stroke-width: 2.5; stroke-dasharray: 8 6; marker-end: url(#return); }
|
|
.build { fill: none; stroke: #a16207; stroke-width: 2.5; stroke-dasharray: 5 5; marker-end: url(#build); }
|
|
</style>
|
|
</defs>
|
|
<rect width="1800" height="1420" fill="#f6fafd"/>
|
|
|
|
<rect x="48" y="44" width="68" height="68" fill="#cbd5e1"/>
|
|
<rect x="44" y="40" width="68" height="68" fill="#e0f2fe" stroke="#bae6fd"/>
|
|
<path d="M59 71h8V59h21v7h11v21H59z" fill="#7dd3fc"/><path d="M82 50h13v13H82z" fill="#f5c66b"/>
|
|
<text x="138" y="79" class="title">OpenCloud · 当前系统架构</text>
|
|
<text x="140" y="111" class="body" style="fill:#64748b">运行时请求、认证与开发期契约同步 / 2026-09-30</text>
|
|
<path d="M 1255 63 H 1310" class="flow"/>
|
|
<text x="1325" y="70" class="small">调用 / 请求</text>
|
|
<path d="M 1255 100 H 1310" class="response"/>
|
|
<text x="1325" y="107" class="small">JSON 响应</text>
|
|
<path d="M 1515 100 H 1570" class="build"/>
|
|
<text x="1585" y="107" class="small">契约生成 / 同步</text>
|
|
<rect x="40" y="172" width="510" height="832" fill="#edf7fc" stroke="#cbd5e1"/>
|
|
<text x="64" y="213" class="section">01 浏览器 · opencloud</text>
|
|
<text x="64" y="244" class="small" style="fill:#64748b">Vue 3 · Pinia · Vue Router · Vite</text>
|
|
<rect x="680" y="172" width="650" height="832" fill="#f2faf6" stroke="#cbd5e1"/>
|
|
<text x="704" y="213" class="section">02 API 服务 · hono-api</text>
|
|
<text x="704" y="244" class="small" style="fill:#64748b">Hono · Better Auth · Zod · Drizzle</text>
|
|
<rect x="1450" y="172" width="310" height="832" fill="#f8fafc" stroke="#cbd5e1"/>
|
|
<text x="1474" y="213" class="section">03 数据与外部服务</text>
|
|
<text x="1474" y="244" class="small" style="fill:#64748b">持久化、图片与邮件</text>
|
|
<rect x="69" y="282" width="460" height="104" fill="#cbd5e1"/>
|
|
<rect x="65" y="278" width="460" height="104" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="87" y="314" class="node">页面 / 组件 / Pinia stores</text>
|
|
<text x="87" y="346" class="body">交互、加载状态、业务缓存</text>
|
|
<text x="87" y="373" class="code">src/features/</text>
|
|
<rect x="69" y="435" width="460" height="123" fill="#cbd5e1"/>
|
|
<rect x="65" y="431" width="460" height="123" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="87" y="467" class="node">业务 API 模块</text>
|
|
<text x="87" y="499" class="code">clouds/api.ts · profile/api.ts · admin/api.ts</text>
|
|
<text x="87" y="526" class="body">路径、分页、批量分块、响应适配</text>
|
|
<text x="87" y="553" class="code">api-adapters.ts → 前端视图模型</text>
|
|
<rect x="69" y="614" width="460" height="122" fill="#cbd5e1"/>
|
|
<rect x="65" y="610" width="460" height="122" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="87" y="646" class="node">HTTP 传输层</text>
|
|
<text x="87" y="678" class="code">src/lib/api.ts</text>
|
|
<text x="87" y="705" class="body">Cookie · 查询编码 · JSON / FormData</text>
|
|
<text x="87" y="732" class="body">ApiError · 401 认证失效事件</text>
|
|
<path d="M 295 382 V 431" class="flow"/>
|
|
<path d="M 295 554 V 610" class="flow"/>
|
|
<text x="66" y="775" class="small" style="fill:#64748b">浏览器媒体元素直接请求 /images/:id/:variant</text>
|
|
<rect x="69" y="828" width="460" height="111" fill="#cbd5e1"/>
|
|
<rect x="65" y="824" width="460" height="111" fill="#fff" stroke="#94b8aa"/>
|
|
<text x="87" y="860" class="node">独立认证客户端</text>
|
|
<text x="87" y="892" class="code">authClient.ts → Better Auth SDK</text>
|
|
<text x="87" y="919" class="body">登录 / 注册 / 会话 / 邮箱验证 / 密码</text>
|
|
<text x="66" y="976" class="small" style="fill:#64748b">AMap:浏览器按需加载地图 SDK</text>
|
|
<rect x="709" y="282" width="600" height="104" fill="#cbd5e1"/>
|
|
<rect x="705" y="278" width="600" height="104" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="727" y="314" class="node">统一响应序列化</text>
|
|
<text x="727" y="346" class="code">src/serializers.ts</text>
|
|
<text x="727" y="373" class="body">Cloud / CloudType / User → JSON · ISO UTC</text>
|
|
<rect x="709" y="435" width="600" height="123" fill="#cbd5e1"/>
|
|
<rect x="705" y="431" width="600" height="123" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="727" y="467" class="node">业务路由 + 按需复用服务</text>
|
|
<text x="727" y="499" class="code">/clouds · /cloud-types · /profiles · /admin</text>
|
|
<text x="727" y="526" class="body">Zod 请求校验 · 会话、角色与所有权校验</text>
|
|
<text x="727" y="553" class="body">Drizzle 查询 · 分页 / 点赞 / 压缩 / 存储</text>
|
|
<rect x="709" y="614" width="600" height="122" fill="#cbd5e1"/>
|
|
<rect x="705" y="610" width="600" height="122" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="727" y="646" class="node">Hono HTTP 入口</text>
|
|
<text x="727" y="678" class="code">src/index.ts</text>
|
|
<text x="727" y="705" class="body">CORS · request ID · 计时 · 错误处理</text>
|
|
<text x="727" y="732" class="body">资源路由分发;/images 代理图片流</text>
|
|
<path d="M 1005 610 V 554" class="flow"/>
|
|
<text x="1025" y="590" class="small" style="fill:#34745d">分发 / 校验</text>
|
|
<path d="M 1005 431 V 382" class="flow"/>
|
|
<text x="1025" y="412" class="small" style="fill:#34745d">查询结果</text>
|
|
<path d="M 525 669 H 705" class="flow"/>
|
|
<text x="554" y="626" class="small" style="fill:#34745d">HTTPS</text>
|
|
<text x="553" y="649" class="small" style="fill:#34745d">携带 Cookie</text>
|
|
<path d="M 705 323 H 612 V 704 H 525" class="response"/>
|
|
<text x="568" y="300" class="small" style="fill:#0284c7">响应</text>
|
|
<text x="706" y="775" class="small" style="fill:#64748b">JSON 业务协议与 Better Auth 原生协议分别处理</text>
|
|
<rect x="709" y="828" width="600" height="111" fill="#cbd5e1"/>
|
|
<rect x="705" y="824" width="600" height="111" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="727" y="860" class="node">Better Auth · /auth/*</text>
|
|
<text x="727" y="892" class="code">HttpOnly session cookie · Drizzle adapter</text>
|
|
<text x="727" y="919" class="body">认证表与业务表共用同一个 PostgreSQL</text>
|
|
<path d="M 525 880 H 705" class="flow"/>
|
|
<text x="554" y="861" class="code">/auth/*</text>
|
|
<text x="706" y="976" class="small" style="fill:#64748b">事务外图片存储:串行写入,上传失败执行补偿清理</text>
|
|
<rect x="1474" y="282" width="270" height="142" fill="#cbd5e1"/>
|
|
<rect x="1470" y="278" width="270" height="142" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="1492" y="314" class="node">PostgreSQL</text>
|
|
<text x="1492" y="346" class="body">用户 / 会话 / 凭据</text>
|
|
<text x="1492" y="373" class="body">云类型 / 云图 / 点赞</text>
|
|
<text x="1492" y="400" class="small">业务与认证共同使用</text>
|
|
<path d="M 1305 469 H 1394 V 350 H 1470" class="flow"/>
|
|
<text x="1360" y="326" class="small" style="fill:#34745d">Drizzle</text>
|
|
<rect x="1474" y="492" width="270" height="142" fill="#cbd5e1"/>
|
|
<rect x="1470" y="488" width="270" height="142" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="1492" y="524" class="node">对象存储</text>
|
|
<text x="1492" y="556" class="body">MinIO / S3 兼容接口</text>
|
|
<text x="1492" y="583" class="code">original + preview</text>
|
|
<text x="1492" y="610" class="small">上传、删除、图片读取</text>
|
|
<path d="M 1305 520 H 1415 V 556 H 1470" class="flow"/>
|
|
<text x="1353" y="500" class="small" style="fill:#34745d">storage</text>
|
|
<rect x="1474" y="828" width="270" height="111" fill="#cbd5e1"/>
|
|
<rect x="1470" y="824" width="270" height="111" fill="#fff" stroke="#b6c8d2"/>
|
|
<text x="1492" y="860" class="node">Resend</text>
|
|
<text x="1492" y="892" class="body">验证邮箱 / 密码重置</text>
|
|
<text x="1492" y="919" class="code">services/email.ts</text>
|
|
<path d="M 1305 880 H 1470" class="flow"/>
|
|
<text x="1341" y="861" class="small" style="fill:#34745d">发送邮件</text>
|
|
<text x="1471" y="687" class="small" style="fill:#64748b">公开可见规则</text>
|
|
<text x="1471" y="718" class="body">审核通过且未隐藏</text>
|
|
<text x="1471" y="747" class="small" style="fill:#64748b">上传者 / 管理员另行授权</text>
|
|
<rect x="40" y="1040" width="1720" height="266" fill="#fffbef" stroke="#e6d7b0"/>
|
|
<text x="65" y="1082" class="section">04 开发期 · 生成契约,阻止前后端类型漂移</text>
|
|
<text x="65" y="1113" class="body" style="fill:#64748b">只同步 TypeScript 类型;前端独立构建使用已提交快照,不加载后端运行时依赖。</text>
|
|
<rect x="69" y="1154" width="340" height="113" fill="#cbd5e1"/>
|
|
<rect x="65" y="1150" width="340" height="113" fill="#fff" stroke="#d6bf82"/>
|
|
<text x="87" y="1186" class="node">后端 Zod 定义</text>
|
|
<text x="87" y="1218" class="code">hono-api/src/schemas/</text>
|
|
<text x="87" y="1245" class="body">请求校验 + JSON 响应</text>
|
|
<rect x="584" y="1154" width="330" height="113" fill="#cbd5e1"/>
|
|
<rect x="580" y="1150" width="330" height="113" fill="#fff" stroke="#d6bf82"/>
|
|
<text x="602" y="1186" class="node">后端生成物</text>
|
|
<text x="602" y="1218" class="code">hono-api/contracts/api.ts</text>
|
|
<text x="602" y="1245" class="small">contract:check 检查过期</text>
|
|
<rect x="1074" y="1154" width="405" height="113" fill="#cbd5e1"/>
|
|
<rect x="1070" y="1150" width="405" height="113" fill="#fff" stroke="#d6bf82"/>
|
|
<text x="1092" y="1186" class="node">前端契约快照</text>
|
|
<text x="1092" y="1218" class="code">src/types/api-contract.ts</text>
|
|
<text x="1092" y="1245" class="body">业务 API 模块引用这些类型</text>
|
|
<path d="M 405 1207 H 580" class="build"/>
|
|
<text x="421" y="1185" class="code">contract:generate</text>
|
|
<path d="M 910 1207 H 1070" class="build"/>
|
|
<text x="944" y="1185" class="code">api:sync</text>
|
|
<text x="1510" y="1187" class="body">跨仓库核对</text>
|
|
<text x="1510" y="1220" class="code">api:check</text>
|
|
<text x="1510" y="1248" class="small" style="fill:#64748b">比对两端契约</text>
|
|
<text x="44" y="1357" class="small" style="fill:#34745d">通信约定</text>
|
|
<text x="155" y="1357" class="body">snake_case · ISO UTC · 分页 { items, page, page_size, has_more } · 错误 { message, issues? }</text>
|
|
<text x="44" y="1391" class="small" style="fill:#64748b">当前实现:刷新式加载;批量每次最多 100 个 ID;写请求不自动重试。图中省略页面内部组件与单个数据库索引。</text>
|
|
</svg> |