Skip to content

Releases: jcoder-stack/abp-react-start

v0.4.0

Choose a tag to compare

@jcoder-stack jcoder-stack released this 12 Sep 07:13

设计系统换回 shadcn 原版,只保留自家的蓝;SheetForm 重做;修掉一个让 vite dev 下所有管理页停在骨架屏的老问题。

修复

vite dev 管理页不水合#21)。tls-trust.tsproxy 子路径顶层静态 import "node:fs";starter 的 @/auth 桶文件把路由守卫和服务端 runtime 放在一起导出,于是每个页面都把 proxy 拖进客户端模块图,浏览器里整棵 @/auth 求值失败,根组件永远水合不了。自 0.1.2 起就是这样,SSR 骨架屏掩盖了它。

现在 node 内建模块在调用时经 process.getBuiltinModule 取——installExtraCa 只在服务端被调,没有该 API 的运行时(Node < 22.3)本来也没有 CA API,照旧返回 "unsupported"CULTURE_COOKIE 挪到 auth 子路径(proxy 继续 re-export,公共 API 不变)。examples/starter 客户端构建的四条 "externalized for browser compatibility" 警告随之清零。

主题:退回 shadcn 原版 + 自家的蓝

app-theme.css 381 → 300 行。shadcn blue preset 原样采用,相对原版每主题只差三个 token--primary--primary-foreground--ring。自家这支蓝在暗色下变亮(0.55 → 0.57),一个值同时能当填充、链接与焦点环,两个主题都过 WCAG AA;preset 那支在暗色下压在近黑页底上只有 2.24:1。

保留的只有原版没覆盖的东西:CJK 字体回退栈、--text-sm 13px(唯一的密度杠杆)、status-* / brand-* 语义色、降低动效偏好块。删掉的:自定义刻度、510/590 字重、逐档负字距、四级 surface 阶梯、五层模态阴影、整个 :lang(zh) 覆盖——它们修的都是自定义排版自己造出来的问题。账在 docs/theme-spike.md

SheetForm

  • 标题领衔记录:新建态给宾语(「新建图书」),编辑/查看态以记录本身为标题、次级标识(邮箱、作者 · 日期)放副标题。useAbpSheet 新增 title / subtitle 解析器。
  • 四条关闭路径(Esc、右上角 ×、遮罩、取消)收敛到一道闸门:有未保存改动先问「放弃未保存的修改?」。useAbpSheet 上报 dirty
  • 标「选填」而非红星,由表单层决定(OptionalMarks context):录入表单多数必填、标少数派;查询表单全是选填、不标。红色留给错误。
  • 查看态渲染成定义列表(divide-y rounded-lg border bg-card、值右对齐、行高与表格同源),不再是一排禁用输入框。每个字段给出自己的 display;未接的字段回退到禁用控件,不会碎。
  • 说明文字放标签下、控件上;控件下方永远留给错误。底栏单行右对齐,取消用 ghost。

表格

  • 行高改为声明值(h-10 / 紧凑 h-8),此前由行尾操作按钮撑出,同一档里带菜单的行比纯文本行高 12px。
  • col.bool 默认渲染普通文字(否为 muted),只有传了 trueStatus 才出徽章。徽章的含义是「处于某状态」,isDefault / isPublic 是属性——此前同一个「是」在角色页是蓝色、在用户页是绿色。用户页 isActive 仍显式传 success

文档

DESIGN.md 785 → 236 行。token 数值只在主题文件里,文档讲「相对 shadcn 原版改了什么、为什么」、原版没覆盖的产品约定、以及每条定制层规则的一行理由。四份分析文档随行:docs/design-review.mdsheetform-review.mdsheetform-design.mdtheme-spike.md

升级注意

  • useAbpSheettitle / subtitle 可选,不传维持「新增 / 编辑 / 查看」。
  • col.bool 的默认观感变了:想保留徽章,显式传 trueStatus
  • required-mark.tsx 改名 optional-mark.tsxform 块新增 alert-dialog 依赖,jc-abp add form 会自动带上。
  • 若你覆盖过 app-theme.css 里的自定义刻度类(text-2xs 之外的自定义档)或 :lang(zh) 规则,它们已不存在。

v0.3.0

Choose a tag to compare

@jcoder-stack jcoder-stack released this 07 Sep 05:36

代理层现在收得下 multipart 上传与裸字节直传。ABP [FromForm] 的导入端点此前拿到的是一个没有正文的请求。

修复

AbpProxyRequest.body 的类型是 string,而 abpFetchtypeof init?.body === "string" ? init.body : undefined 收窄调用方的 body——传 FormData 不报错,静默变成空 body。四层链路(abpFetchabpRequestFn 的 zod schema → AbpProxyRequestproxy.send)都把 body 钉在 string 上。

新增

AbpProxyRequest.body 拓宽为 string | Uint8Array | ArrayBuffer | FormData,新导出类型 AbpProxyBody

判据是可重发而不是「是不是二进制」:代理的核心能力是 401→刷新→重放与幂等重试,两者都要把同一个 body 再发一次。ReadableStream 刻意排除,并在客户端与代理两处显式拒绝——它只能消费一次,收下它会让那两条路径静默退化成「重放一个空正文」。

新增 server function abpUploadFn,二进制以原生 multipart 过桥,不进 JSON 载荷。这是量过之后的选择:

走法 实测
Uint8Array 交给 seroval 1MB 反序列化抛 SerovalDeserializationError(512KB 尚可)
base64 成字符串 10MB 能过,但文件变 13.3MB 字符串、两端各解析一次 JSON
原生 multipart 文件字节全程不进 JSON

TanStack Start 要求整个 payload 就是 FormData,混不进 JSON 对象,故为第二个 server fn。文本那条路径的 zod schema 一个字未动,所有既有 CRUD 调用不受影响

createAbpProxy 新增 maxBodyBytes(默认 10MB),在任何网络动作之前拦截。

一个会让人查很久的坑

body 是 FormData 时,调用方的 content-type 现在会被丢弃。它带的是调用方那份 FormData 的 boundary,而传输层重新编码时会生成新的;透传旧值会让上游按错的分隔符解析,一个字段都读不出来——症状是「请求到了、参数全空」,不是报错。content-type 本就在转发白名单内,所以这条不处理必然踩。

使用

调用方无需改动写法,abpFetch 对外仍是 typeof fetch

const form = new FormData();
form.append("file", file, file.name);
await abpFetch("/api/app/books/import", { method: "POST", body: form });

裸字节直传同样可用(content-type 按需自带):

await abpFetch("/api/app/files/1", {
  method: "PUT",
  headers: { "content-type": "application/octet-stream" },
  body: bytes,
});

升级须知

@jcoder-stack/registry 的分发物变了:jc-abp add app-shell 装到的 src/api/abp-fetch.ts 是新版,且 jc-abp init 会多铺一个 src/auth/upload-payload.ts。已有项目要吃到这个能力需重新 add / initAbpProxyRequest.body 是加法拓宽,既有调用无需改动。

验证

97 个测试文件 / 836 用例(新增 14 条)、typecheck(含 @ts-expect-error 类型契约)、lint、三处 build 全通过;registry 分发物已随块源码重建。

v0.2.1

Choose a tag to compare

@jcoder-stack jcoder-stack released this 06 Sep 13:36

纯依赖升级,跟住 TanStack 上游。零源码改动,无破坏性变更。

依赖升级

@tanstack/react-table 9.1.2 9.2.4
@tanstack/react-router 1.170.24 1.170.32
@tanstack/react-start 1.168.41 1.168.49
@tanstack/react-query 5.101.2 5.102.8
@tanstack/react-form 1.33.2 1.33.5
@tanstack/react-router-ssr-query 1.167.1 1.167.2
router-plugin / router-cli 1.168.28 / 1.167.21 1.168.35 / 1.167.33
react-devtools / devtools-vite 0.10.8 / 0.8.1 0.10.12 / 0.8.5

对使用者的实际影响

registry.json 里块依赖清单的 react-table9.1.2 提到 9.2.4——这是 jc-abp add 写进你项目的版本,所以 data-table / abp-table / admin-pages 三个块装出来会带新版。已有项目不受影响,重装或新装才会拿到。

升级真正吃到的是 router 那段,每次导航都走:

  • 1.170.28 重访已解析的代码分割路由时复用已加载组件,不再多闪一次 pending;同版避免了 SSR/水合期把可能渲染 <html> 的根组件包进 Suspense
  • 1.170.30 pending UI 跨保留路由不丢
  • 1.170.32 reload 期间 context 不被清掉

react-table 9.1.2 → 9.2.4 对本项目是空转:两个补丁只碰层级 flatRows 排序与 worker 行模型,而 table-core.ts 只注册 sorting / pagination / visibility / selection,进不到执行路径。升它是为了跟住上游。

@tanstack/react-query 5.102.0 修了 queryOptions 返回类型泄漏内部 tag symbol 导致声明文件生成失败——对要出 .d.ts@jcoder-stack/abp-react 是白捡的。

检查过但未踩到的上游移除

  • Link 上未文档化的 isTransitioning 状态与 data-transitioning 属性(router 1.170.26 移除)——全仓库含 CSS 无引用
  • Query 的实验性 render-time prefetch 与查询结果上的 promise(5.102.0 移除)——未使用
  • router 1.170.26 新增「在 pending/error/not-found 组件里渲染 Outlet 会告警」——shell-boundarysection-boundary 都没有 Outlet

@tanstack/react-form 1.33.5 仍未修复 submit 字段级错误不自清的死锁,useAppFormclearServerSubmitErrors patch 保留;中英两份 forms 指南的版本事实已同步。

维护

scripts/regenerate-example.sh 的重放 pin 表随 starter manifest 同步(docs/releasing.md 列为升级依赖时的必改项);漏改会让下次重放把 TanStack 依赖悄悄打回旧版,而 bun install 与测试都看不见。

v0.2.0

Choose a tag to compare

@jcoder-stack jcoder-stack released this 11 Aug 00:34

依赖升级到 TanStack Table v9,新增列预设,表格搜索与勾选不再触发整表重渲染。

依赖升级

  • @tanstack/react-table 8.x → 9.1.2:块内部改用 useTable + tableFeatures 特性注册
  • @tanstack/react-router1.170.24@tanstack/react-start1.168.41

新增

  • 列预设 createAbpColumns<TData>()components/abp/table/column-presets.tsx):text / date / money / enum / bool 五个预设,外加 accessor / display / group 透传与 columnHeader(label)。label 传词条 key 而非译文,列数组可以放到模块级
  • 按表声明的 columnMetatable-core.tsTableColumnMeta + baseFeatureMap),取代全局 declare module 增广;下游可传 columnMeta: {} as TableColumnMeta & { mine: X } 追加自己的键
  • 表实例暴露 getSelectedRows / clearSelection / keepSelected / SelectedCount / selectable
  • docs/releasing.md / releasing.en.md:发版流程
  • publish-smoke.sh 增加跨包依赖区间检查;新增 registry 与 starter 块源码一致性测试

性能

搜索输入与行勾选不再重渲染整表。搜索状态下沉到 toolbar,勾选状态交还给表实例并按行 SubscribeuseTable 宿主选择器排除 rowSelection。100 行实测:

交互 之前 之后
输入搜索 42.3 ms/commit 4.3 ms/commit
勾选一行 30.2 ms/commit 3.7 ms/commit

两项均不再随行数增长。

修复

  • 第 1 页切换查询条件(搜索、排序、筛选)后残留旧勾选,批量删除会作用到上一批数据
  • 文档与 TSDoc 不再指向已废弃的 table.store;修正 CellTableInstance 的 API 指引(该类型上没有 state / Subscribe

破坏性改动

Table 块的勾选与搜索 API 有变化,selectable: true 是所有批量能力的前提。迁移对照见 docs/guides/abp-table.md

升级

bun add @jcoder-stack/abp-react@0.2.0
bun add -D @jcoder-stack/cli@0.2.0 @jcoder-stack/registry@0.2.0
bunx jc-abp add abp-table

v0.1.3

Choose a tag to compare

@jcoder-stack jcoder-stack released this 05 Aug 04:01

两个真机实测暴露的 bug,加一整套错误处理收口。全部改动都先在真实浏览器里复现、修复后再验证。

修复:Sheet 里的 combobox 点不中(影响所有抽屉表单的下拉选择)

编辑用户时无法选角色、编辑 book 时无法选作者——下拉打开、选项可见,鼠标点了没反应(键盘反而可用)。根源是 Radix modal(Sheet)打开时把 body 设为 pointer-events: none 且只恢复自己的内容,而 Base UI 的 combobox 弹层 portal 在 dialog 之外,整条链没有一层恢复。主题层一条规则修复:

[data-slot="combobox-content"] { pointer-events: auto; }

这类缺陷 jsdom 表达不出来(合成事件直达元素),组件测试全绿——只有真浏览器能抓到。

修复:/forbidden 在生成的应用里是 404

requirePermission 无权限时跳 /forbidden,但这个页面此前只存在于参照应用,从未随 app-shell 分发——权限守卫的跳转目标是个 404。现已分发;403/404 页同时加上注销链接:困在无权限页面的用户通常需要换账号,只给「返回首页」是死路。

新增:控制台首页 /home——控制台入口不再 403

「进入控制台」此前硬编码指向 /identity/users,没有 Identity 权限的用户一进门就 403;侧栏「Home」还会跳出控制台回落地页。现在:

  • 新增 _layout/_authed/home.tsx:登录即可见、零权限要求——欢迎语(带用户名)、标语、特性卡,词条全部就位,按提示改成你自己的首页即可
  • 「进入控制台」、侧栏「Home」、forbidden 的「返回首页」全部指向 /home
  • 营销落地页继续独占 /

新增:错误边界成体系

  • 路由级默认边界:createRouter 带上 defaultErrorComponent / defaultNotFoundComponent——你新增的业务页出错时,错误渲染在页面自己的位置、侧栏保持在位,不必每页手动挂 errorComponent(某页要定制时显式声明依然优先)
  • SectionBoundary(新组件):包住页面里的一个区块,渲染抛错只替换该区块,兄弟区块照常;默认 fallback 词条化 + 重试,可自定义
  • 静态兜底:错误页自身再抛错时(唯一出场时机),白屏降级为零依赖的静态提示,而不是整树卸载

改进:表格工具条

刷新 / 导出 / 密度 / 列 / 排序 / 筛选六颗图标钮都有了 tooltip。刷新的语义顺带说明:带条件刷新(对当前查询含筛选重新请求),重置在筛选面板的 Reset 上。

升级

bun add @jcoder-stack/abp-react@0.1.3
bun add -D @jcoder-stack/cli@0.1.3 @jcoder-stack/registry@0.1.3

已初始化的项目按需取块的新版本(会覆盖你对块文件的本地改动,注意先提交):

npx shadcn@4.13 add node_modules/@jcoder-stack/registry/public/r/app-shell.json --overwrite
npx shadcn@4.13 add node_modules/@jcoder-stack/registry/public/r/data-table.json --overwrite

combobox 修复在主题 css 里——已有项目在 src/styles.css 追加上面那条 [data-slot="combobox-content"] 规则即可,或整体替换为新版 app-theme.css。router 的默认边界两行参照 README 的 router 说明手动加上(init 只在初始化时接线)。

v0.1.2

Choose a tag to compare

@jcoder-stack jcoder-stack released this 04 Aug 11:56

这个版本解决一件事:本地起步的头三堵墙,现在每一堵都有清晰的出路——自签证书、后端地址配置、.env 没填全。顺带把 npm 包文档换成英文,并新增全套英文文档。

自签证书:.env 一行解决

本地 ABP 的自签证书在系统钥匙串里受信,但 Node 不读钥匙串——此前 gendev 都要在启动命令上带 NODE_EXTRA_CA_CERTS(它没法写进 .env,Node 只在启动时读一次)。现在:

dotnet dev-certs https --export-path ~/.aspnet-dev.crt --format PEM
# .env
AUTH_EXTRA_CA_FILE=~/.aspnet-dev.crt

auth 运行时与 gen 都会在第一笔上游请求前把这张证书追加进进程默认 CA(tls.setDefaultCACertificates,运行时 API,需要 Node ≥ 22.15),其余站点照常校验。旧运行时(更早的 Node、Bun 直跑服务端)自动降级为日志提示回退方案。

init 引导后端配置

$ npx jc-abp init
ABP backend URL (e.g. https://localhost:44316, Enter to skip):
  • 一个地址派生三处:AUTH_ISSUERAUTH_ABP_BASE_URLabp.api.config.ts 的 swagger input
  • .env 自动生成(自 .env.example 派生),AUTH_SESSION_SECRET 随机生成——不再手跑 openssl rand
  • 回车跳过、非 TTY 自动跳过、--backend <url> 供脚本/CI
  • init 从不连接后端,后端没启动不影响初始化;收尾做一次 3 秒探测,只提示不阻断——连不上提醒 gen 前启动后端,证书不受信直接指出该配 AUTH_EXTRA_CA_FILE(顺带证明地址没填错)

报错全部指向下一步

场景 之前 现在
.env 缺失/留空启动 ZodError 堆栈 auth env is missing or invalid: AUTH_CLIENT_ID (not set). Fill these in the project's .env …
运行时证书不受信 fetch failed 指名 AUTH_EXTRA_CA_FILE 的完整说明,且不再徒劳重试
运行时后端连不上 fetch failed(重试后) cannot reach the ABP backend (ECONNREFUSED) … The backend is not running, or AUTH_ABP_BASE_URL in .env points at the wrong place
gen 连不上后端 fetch failed 按证书/不可达/404 分类,各给出路

CLI 修复与英文化

  • 修复:交互终端下 init 会挂在 npx 自己的下载确认上(Ok to proceed? 被进度输出淹没,看起来像挂死;非 TTY 从不触发,所以此前未暴露)。npx 调用现在带 -y
  • CLI 的全部用户可见输出(usage、交互提问、进度行、错误信息、收尾指南)改为英文;app-env.d.ts 声明的变量名与真实契约(AUTH_*)对齐。

文档

  • npm 三个包的 README 与 description 换成英文(npmjs.com 直接渲染)
  • 仓库新增英文全套:README.en.md + architecture 与全部四份 guides 的 .en.md 版本,中英互链
  • .env.example 占位端口与 config 模板统一为 44316

升级

bun add @jcoder-stack/abp-react@0.1.2
bun add -D @jcoder-stack/cli@0.1.2 @jcoder-stack/registry@0.1.2

无破坏性改动。已初始化的项目不需要重跑 init;升级 @jcoder-stack/abp-react 即获得运行时的全部新报错与 AUTH_EXTRA_CA_FILE 支持(该变量按需加进你的 .env 即可)。

v0.1.1

Choose a tag to compare

@jcoder-stack jcoder-stack released this 02 Aug 11:10
37f5e9b

本地 ABP 后端用自签证书时,bun run dev 打开首页得到 500,页面上只有一句 fetch failed。这个版本让它说人话,并把文档里缺的那半边补上。

为什么会这样

TLS 那一跳的客户端是 Node,不是浏览器:

浏览器 ──HTTP──> localhost:3000 (TanStack Start / Node)
                        └──HTTPS──> localhost:44316 (ABP)   ← TLS 在这里

dotnet dev-certs https --trust 把证书装进系统钥匙串,浏览器与 curl 因此放行。Node 不读钥匙串,只校验自己内置的 CA 列表,于是拒绝 CN=localhost 这张自签证书。

CA 来源 结果
curl / 浏览器 系统钥匙串 200
Node 内置 Mozilla 列表 DEPTH_ZERO_SELF_SIGNED_CERT
Node + NODE_EXTRA_CA_CERTS 内置列表 + 这一张 200

怎么办

dotnet dev-certs https --export-path ~/.aspnet-dev.crt --format PEM

NODE_EXTRA_CA_CERTS=~/.aspnet-dev.crt npx jc-abp gen
NODE_EXTRA_CA_CERTS=~/.aspnet-dev.crt bun run dev

这个变量不能写进 .env Node 在进程启动时读它,dotenv 加载时已经太晚——写了不生效,且没有任何提示。放命令上,或 export 进 shell profile。

NODE_TLS_REJECT_UNAUTHORIZED=0 同样能跑通,写进 .env 也有效,但它关掉的是整个进程对所有主机的证书校验。临时试可以,别留在会被复制到服务器的文件里。

改了什么

@jcoder-stack/abp-react —— 代理识别 OpenSSL 的证书信任失败码后:

  • 不再重试。 证书不受信是确定性失败,原先的两次重试只是把同一次握手重做两遍,还把原因埋在退避后面。
  • 给出可执行的说明,指名 NODE_EXTRA_CA_CERTS 与被拒的 origin,原始错误保留在 cause 上。

@jcoder-stack/registry —— .env.example 补上运行时的这条要求,并写明上面两个变量的差异。

@jcoder-stack/cli —— init 结束时打印的接线提示同样覆盖 dev,不再只提 gen

文档 —— 初始化指南新增「自签证书的本地后端」一节,排障表补两行,starter README 与 .env.example 同步。此前全部 5 处 TLS 说明都只在 gen 的语境下,运行时只字未提,这正是踩坑的直接原因。

升级

bun add @jcoder-stack/abp-react@0.1.1
bun add -D @jcoder-stack/cli@0.1.1 @jcoder-stack/registry@0.1.1

没有破坏性改动。已经装好块的项目不需要重跑 init——想拿新的 .env.example 注释,从 node_modules/@jcoder-stack/registry/auth/root/.env.example 对照即可。

v0.1.0

Choose a tag to compare

@jcoder-stack jcoder-stack released this 02 Aug 06:45

ABP React Start 的第一个版本。给 ABP 后端配一套现代 React 前端:OIDC 登录经 BFF 代理、加密 cookie 会话、权限、多租户、本地化,以及一组直接读 ABP 协议的 CRUD 原语。

一条命令落地一个能跑的应用,然后它就退场——给你的是源码,不是运行时依赖。UI 全在你自己的仓库里,改它不需要绕过任何抽象。

装什么

npx @tanstack/cli create my-app && cd my-app

bun add @jcoder-stack/abp-react
bun add -D @jcoder-stack/cli @jcoder-stack/registry

npx jc-abp init
# 改 .env 与 abp.api.config.ts 指向你的后端
npx jc-abp gen && bun run dev

jc-abp init 会播种基线配置与主题、落认证外壳、按依赖序装齐 13 个块、接好 __root.tsxrouter.tsx、生成路由树。装完是 85 个源码文件在你的 src/ 下。

三个包

作用
@jcoder-stack/abp-react 运行时内核。子路径导出 core / auth / proxy / react / router / permissions / i18n / logger
@jcoder-stack/cli jc-abp init / add / gen(命令名不带 scope)
@jcoder-stack/registry 13 个 shadcn 块与认证外壳的 copy-in 源,装成 devDependency

块:abp-layoutabp-loginapp-shelldata-tableformcomboboxdate-pickertreeabp-crudabp-tableabp-sheetabp-permission-sheetadmin-pages(用户 / 角色 / 租户 / 设置 / 个人资料五页)。

装一个块会把它的前置块一并带上,不必自己排顺序。

前置

  • Node ≥ 18(gen.ts 配置需要 Bun 或 Node ≥ 22.18)
  • bun 或 npm。npmrc 里配了 allow-scripts 时请用 bun:npx 会把它注入子进程,npm 拒绝项目级安装,shadcn 装块依赖必然失败。init 检测到这种情况会拒绝开工并说明原因
  • 一个 ABP 后端。gen 读它的 swagger 生成 API 客户端
  • peer:@tanstack/react-router ^1.170.18react ^18 || ^19zod ^4

外部 CLI 锁在已验证的 minor:shadcn@4.13@tanstack/router-cli@1.167

已知约束

@tanstack/react-table 用的是 9.0.0-beta.59 data-table 块依赖 v9 才有的 tableFeatures / rowSortingFeature 等 API,块里已钉死这个版本。v9 正式发布前,升级它会编译失败。

@jcoder-stack/registry 必须装在项目根。 块之间用 ./node_modules/@jcoder-stack/registry/public/r/*.json 互相引用,这条路径按消费项目的根解析。npm 与 yarn classic 的 workspace 会把它提升到 workspace 根,成员目录下就找不到——那种项目请按依赖表自己排序逐块装。jc-abp init 不受影响,它自己解析真实路径。bun 与 pnpm 会在成员目录建符号链接,无此问题。

CRUD 相关的块要先跑 jc-abp gen abp-crud / abp-table / abp-sheet / abp-permission-sheet / admin-pages 都 import @/api/*,那是 gen 的产物。装完不跑 gen,编译不过。

本地后端用自签证书时 gen 会报 fetch failed Node 不读系统钥匙串,证书在钥匙串里受信也没用。加 NODE_TLS_REJECT_UNAUTHORIZED=0 即可。

.env.example 里的 AUTH_CLIENT_IDAUTH_ISSUER 是占位。 client 必须是你后端 OpenIddict 里已注册的那一个,回调地址也要登记在它名下;scope 里还要带后端的 API 资源 scope,漏了登录能过但每个管理页都是空的。.env.example 的注释里给了探测方法。

权限名显示成原始 key 是后端的本地化缺口。 权限树用的是后端返回的 displayName,ABP 查不到词条就原样返回 key。补你后端对应资源的本地化文件即可。

安全

会话密封、OIDC 握手、BFF 代理是本项目自己承担的部分,漏洞请走 GitHub 的私密报告(Security → Report a vulnerability),不要开公开 issue。权限助手只管界面显隐,真正的授权在 ABP——前端能被绕过是设计如此。详见 SECURITY.md

文档

从零初始化 · 按需装块 · 列表页与 CRUD · 表单四层 · 架构

参照应用 examples/starter 就是这套流程的产物,由脚本重放「脚手架 → 装包 → init → gen」得到,既是活文档也是端到端回归。