@_tc/template-core 0.0.1-bate.51 → 0.0.1-bate.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.skills/tc-generator/reference/runtime-extensions.md +30 -0
- package/AGENT_README.md +19 -1
- package/README.md +49 -1
- package/cjs/bundler/defaultRouteGuard.js +1 -0
- package/cjs/bundler/utils.js +1 -1
- package/cjs/packages/core/loader/router.js +1 -1
- package/esm/bundler/defaultRouteGuard.js +4 -0
- package/esm/bundler/utils.js +10 -4
- package/esm/packages/core/loader/router.js +1 -1
- package/fe/bundler/defaultRouteGuard.js +4 -0
- package/fe/frontend/apps/dash/Dashboard.js +21 -0
- package/fe/frontend/apps/dash/types.d.ts +11 -0
- package/fe/frontend/src/common/auth/index.d.ts +1 -0
- package/fe/frontend/src/common/auth/index.js +5 -2
- package/fe/frontend/src/common/request.js +1 -1
- package/model/frontend/src/common/auth/index.d.ts +1 -0
- package/package.json +1 -1
- package/types/bundler/defaultRouteGuard.d.ts +2 -0
|
@@ -109,6 +109,20 @@ export default components
|
|
|
109
109
|
|
|
110
110
|
Keep header content compact; the default header already owns brand, title, and menu layout.
|
|
111
111
|
|
|
112
|
+
## Server Router Guard
|
|
113
|
+
|
|
114
|
+
Use `app/router-guard.js` or `app/router-guard.ts` in the generated business project when unmatched server paths need custom fallback behavior. The file lives directly under the business `app/` directory.
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
module.exports = (app) => (ctx) => {
|
|
118
|
+
if (!ctx.path.startsWith(app.options.apiPrefix || '/api')) {
|
|
119
|
+
return '/dash'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The guard runs only from the final catch-all router after normal `app/router/*` routes fail to match. Return a string to redirect, return `{ status, body }` to send a custom response, or return nothing to keep the default fallback: API paths return HTTP 200 with `{ code: 404, message }`, while non-API paths return HTTP 404 with `notFound`.
|
|
125
|
+
|
|
112
126
|
## Dashboard Custom Routes
|
|
113
127
|
|
|
114
128
|
Use `frontend/extended/dash/customRoutes.tsx` when a `custom` menu item needs a route inside the built-in Dashboard.
|
|
@@ -133,6 +147,22 @@ export default extendDashRoutes
|
|
|
133
147
|
|
|
134
148
|
`topRoutes` are appended under `/dash`. `sidebarRoutes` are appended inside the default sidebar containers.
|
|
135
149
|
|
|
150
|
+
## Dashboard Route Guard
|
|
151
|
+
|
|
152
|
+
Use `frontend/extended/dash/routeGuard.ts` when the built-in Dashboard needs a one-time guard before its default `homePage` or first-menu redirect.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import type { DashRouteGuard } from '@_tc/template-core/fe'
|
|
156
|
+
|
|
157
|
+
const dashRouteGuard: DashRouteGuard = ({ isLoggedIn }) => {
|
|
158
|
+
if (!isLoggedIn) return '/login'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default dashRouteGuard
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Return a login URL string when the framework should jump with `window.location.href`; the login page does not need to be inside Dash routes. `DashRouteGuardResult` is `string | undefined`, and `undefined` also interrupts the built-in Dashboard redirect, including the implicit return from a logged-in branch.
|
|
165
|
+
|
|
136
166
|
## Frontend Helpers
|
|
137
167
|
|
|
138
168
|
Import TemplateCore frontend helpers from `@_tc/template-core/fe`:
|
package/AGENT_README.md
CHANGED
|
@@ -94,6 +94,7 @@ app/controller/**/*.(js|ts) -> app.controller
|
|
|
94
94
|
app/service/**/*.(js|ts) -> app.service
|
|
95
95
|
app/middleware/**/*.(js|ts) -> app.middlewares
|
|
96
96
|
app/router/**/*.(js|ts) -> Koa router
|
|
97
|
+
app/router-guard.(js|ts) -> 兜底路由守卫
|
|
97
98
|
app/router-schema/**/*.(js|ts) -> app.routerSchema
|
|
98
99
|
app/extend/*.(js|ts) -> app.extends
|
|
99
100
|
config/config.default.(js|ts) -> app.config
|
|
@@ -165,6 +166,21 @@ export default (app: KoaApp, router: Router) => {
|
|
|
165
166
|
|
|
166
167
|
内置响应约定:`BaseController.success(ctx, data)` 返回 `{ data, code: 0, message: 'ok' }`。
|
|
167
168
|
|
|
169
|
+
兜底路由守卫:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// app/router-guard.ts
|
|
173
|
+
import type { Ctx, KoaApp } from '@_tc/template-core'
|
|
174
|
+
|
|
175
|
+
export default (_app: KoaApp) => {
|
|
176
|
+
return (ctx: Ctx) => {
|
|
177
|
+
if (!ctx.path.startsWith('/api/')) return '/dash'
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`router-guard` 只在所有正常 router 都没命中时执行。返回字符串会重定向;返回 `{ status, body }` 会直接设置响应;无返回则走默认兜底:API 路径返回 HTTP 200 + `{ code: 404, message }`,非 API 路径返回 HTTP 404 + `notFound`。
|
|
183
|
+
|
|
168
184
|
内置扩展:
|
|
169
185
|
|
|
170
186
|
- `app.extends.$fetch`:Node 侧 axios 风格 fetch 实例。
|
|
@@ -486,7 +502,9 @@ chokidar
|
|
|
486
502
|
|
|
487
503
|
1. SchemaForm 字段类型、CallCom 组件、Koa app 类型都支持 TypeScript 声明合并。
|
|
488
504
|
2. 运行时组件通常放在业务 `frontend/extended/`。
|
|
489
|
-
3.
|
|
505
|
+
3. Dashboard 扩展点包括 `frontend/extended/dash/customRoutes.tsx`、`frontend/extended/dash/components.tsx`、`frontend/extended/dash/routeGuard.ts`。
|
|
506
|
+
4. `dash/routeGuard` 用来判断登录。返回登录页 URL 字符串时,Dashboard 用 `window.location.href` 跳转;`DashRouteGuardResult` 是 `string | undefined`,`undefined`(包括已登录分支的隐式返回)会打断 Dashboard 内置重定向,业务方可自行跳转。
|
|
507
|
+
5. 优先查看公开入口 `.d.ts` 和 npm 包根目录 `README.md`;如果当前环境是源码仓库,再继续读 `docs/type-extension.md` 和 `docs/schema-form.md`。
|
|
490
508
|
|
|
491
509
|
## 8. 继续深入
|
|
492
510
|
|
package/README.md
CHANGED
|
@@ -264,6 +264,7 @@ import type {
|
|
|
264
264
|
CallComComponentsMap,
|
|
265
265
|
CallComRenderer,
|
|
266
266
|
DashComponentsMap,
|
|
267
|
+
DashRouteGuard,
|
|
267
268
|
DashRoutesExtender,
|
|
268
269
|
FormFieldSchema,
|
|
269
270
|
ApiFreezerRequestMatcher,
|
|
@@ -279,7 +280,7 @@ import type {
|
|
|
279
280
|
| --- | --- |
|
|
280
281
|
| 应用启动 | `initApp` |
|
|
281
282
|
| 内置前端组件 | `AsyncSelect`、`LanguageSwitch`、`ThemeSwitch` |
|
|
282
|
-
| Dash 扩展类型 | `DashRoutesExtender`、`DashRoutesContext`、`DashComponentsMap`、`DashHeaderUserAreaProps` |
|
|
283
|
+
| Dash 扩展类型 | `DashRoutesExtender`、`DashRoutesContext`、`DashComponentsMap`、`DashHeaderUserAreaProps`、`DashRouteGuard` |
|
|
283
284
|
| SchemaPage 类型 | `CallComComponentsMap`、`CallComRenderer` |
|
|
284
285
|
| SchemaForm 类型 | `FormFieldSchema`、`SchemaFormComponentsMap`、`SchemaFormNamespace`、`SelectProps` |
|
|
285
286
|
| 请求方法 | `request`、`get`、`post`、`put`、`patch`、`del` |
|
|
@@ -1136,6 +1137,29 @@ DELETE {api} -> 删除
|
|
|
1136
1137
|
|
|
1137
1138
|
`$schema::字段名` 表示从当前表格行读取字段值,常用于删除按钮参数。
|
|
1138
1139
|
|
|
1140
|
+
### 服务端兜底路由守卫
|
|
1141
|
+
|
|
1142
|
+
业务项目可以创建 `app/router-guard.ts` 或 `app/router-guard.js`。它只在所有正常 `app/router/*` 路由都没有命中时执行。
|
|
1143
|
+
|
|
1144
|
+
```ts
|
|
1145
|
+
import type { Ctx, KoaApp } from '@_tc/template-core'
|
|
1146
|
+
|
|
1147
|
+
export default (_app: KoaApp) => {
|
|
1148
|
+
return (ctx: Ctx) => {
|
|
1149
|
+
if (ctx.path.startsWith('/api/')) {
|
|
1150
|
+
return {
|
|
1151
|
+
status: 404,
|
|
1152
|
+
body: { code: 404, message: `${ctx.path} not found` },
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
return '/dash'
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
返回字符串会 `ctx.redirect()`;返回 `{ status, body }` 会直接设置响应;无返回则使用框架默认兜底:API 路径返回 HTTP 200 + `{ code: 404, message }`,非 API 路径返回 HTTP 404 + `notFound`。
|
|
1162
|
+
|
|
1139
1163
|
## 如何扩展组件
|
|
1140
1164
|
|
|
1141
1165
|
### 扩展 Dashboard 顶部用户区域
|
|
@@ -1159,6 +1183,28 @@ export default components
|
|
|
1159
1183
|
|
|
1160
1184
|
- `HeaderView.userArea`:Dashboard 顶部右侧区域。
|
|
1161
1185
|
|
|
1186
|
+
### 扩展 Dashboard 登录守卫
|
|
1187
|
+
|
|
1188
|
+
业务项目可以创建 `frontend/extended/dash/routeGuard.ts`,用于在 Dashboard 内置 `homePage` / 首菜单重定向前做登录判断。
|
|
1189
|
+
|
|
1190
|
+
```ts
|
|
1191
|
+
// frontend/extended/dash/routeGuard.ts
|
|
1192
|
+
import type { DashRouteGuard } from '@_tc/template-core/fe'
|
|
1193
|
+
|
|
1194
|
+
const dashRouteGuard: DashRouteGuard = ({ isLoggedIn }) => {
|
|
1195
|
+
if (!isLoggedIn) return '/login'
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
export default dashRouteGuard
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
返回值规则:
|
|
1202
|
+
|
|
1203
|
+
- 返回 `string`:框架使用 `window.location.href` 跳转,登录页不需要在 Dash 路由下。
|
|
1204
|
+
- 返回非 `string`:中断 Dashboard 内置重定向,业务方可自行跳转或处理提示。
|
|
1205
|
+
- `DashRouteGuardResult` 是 `string | undefined`;`undefined`(包括已登录分支的隐式返回)也会打断 Dashboard 内置重定向。
|
|
1206
|
+
- 没有业务 `routeGuard` 文件时,框架使用兜底空对象,不影响默认 Dashboard 重定向。
|
|
1207
|
+
|
|
1162
1208
|
### 扩展 SchemaForm 字段组件
|
|
1163
1209
|
|
|
1164
1210
|
1. 声明字段类型。
|
|
@@ -1719,5 +1765,7 @@ await buildBE({
|
|
|
1719
1765
|
- SchemaForm 和 CallCom 的类型扩展、运行时注册都要做,少一个就只会“类型通过但页面不显示”或“页面能配但类型报错”。
|
|
1720
1766
|
- `model` 数组合并依赖稳定 `key`。
|
|
1721
1767
|
- Dashboard 默认需要 URL 带 `?projk=项目key`。
|
|
1768
|
+
- Dashboard 登录守卫放在 `frontend/extended/dash/routeGuard.ts`;登录页通常不在 Dash 路由下,返回登录页 URL 即可。
|
|
1769
|
+
- 服务端兜底守卫放在业务 `app/router-guard.ts|js`,不是 `app/business` 子目录。
|
|
1722
1770
|
- 前端入口必须命名为 `.entry.tsx`、`.entry.ts`、`.entry.jsx` 或 `.entry.js`。
|
|
1723
1771
|
- **使用方不建议在 `/frontend/apps/` 下保留 `html` 文件。** Vite 构建时会根据模板文件(`app/view/entry.tpl`)自动生成 html,如果保留 html 文件,可能与模板不同步而导致内容过时。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={};module.exports=e;
|
package/cjs/bundler/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.js`),t=require(`../packages/utils/path.js`);require(`../packages/utils/index.js`);const n=require(`../packages/common/log/index.js`),r=require(`./state.js`);let i=require(`path`);i=e.__toESM(i);let a=require(`glob`);a=e.__toESM(a);let o=require(`fs`),s=require(`vite`),c=require(`@tailwindcss/vite`);c=e.__toESM(c);let l=require(`module`),u=require(`url`);var d=typeof __filename==`string`?__filename:(0,u.fileURLToPath)({}.url),f=i.default.dirname(d),p=process.cwd(),m=(0,l.createRequire)(d),h=i.default.resolve(f,`../../`),g=i.default.resolve(f,`../`),_=i.default.resolve(h,`./fe/frontend`),v=i.default.resolve(p,`.`),y=i.default.resolve(v,`./frontend`),b=e=>i.default.dirname(m.resolve(`${e}/package.json`));function x(){n.logWhite(`frameFEPath: `+_),n.logWhite(`runFEPath: `+y);let e=[_,y].reduce((e,n)=>{let r=[`js`,`jsx`,`ts`,`tsx`].map(e=>t.resolve(n,`/**`,`/*.`+e)),i=a.sync(r).filter(e=>e.includes(`.entry.`)).filter(e=>!e.includes(`.d.ts`));return[...e,...i]},[]),r={};if(!e.length)return r;let s;for(let n=0;n<e.length;n++){let c=e[n],l=i.default.resolve(c,`../`),u=a.sync(t.resolve(l,`*.html`))[0];if(u){let e=i.default.basename(u,`.html`);r[e]=i.default.resolve(p,u)}else{s||=(0,o.readFileSync)(i.default.resolve(g,`./app/view/entry.tpl`),`utf-8`);let e=c.split(i.default.sep).at(-1),t=s.replace(`<!-- {{InsertScript}} -->`,`<script type="module" src="./${e}"><\/script>`),n=l.split(i.default.sep).at(-1),a=i.default.join(l,n+`.html`);(0,o.writeFileSync)(a,t),r[n]=a}}return r}function S(){let e=r.buildFESharedState.options.output??`frame`;return e!==`frame`&&n.logRed(`Please pay attention to the build output.`),e===`run`?i.default.join(v,`./app/public/dist`):typeof e==`function`?e():i.default.join(g,`./app/public/dist`)}var C=async e=>{let t=(await import(`chokidar`)).watch(y,{ignored:/(^|[\/\\])\../,persistent:!0,ignoreInitial:!0}),r=!1;t.on(`change`,async t=>{if(!r){r=!0,n.logYellow(`changed: ${t}`);try{await e?.()}catch(e){n.logRed(e)}finally{r=!1}}}),n.logYellow(`watching ${y}/...`)};function w(e=`.html`){let t=[...new Set(Array.isArray(e)?e:[e])];return{name:`flatten-html`,apply:`build`,closeBundle(){let e=S();if(!(0,o.existsSync)(e))return;let r=a.sync(`**/*.html`,{cwd:e,dot:!0});for(let n of r){let r=i.default.join(e,n),a=i.default.basename(n,`.html`),s=`.entry`;a.indexOf(s)===-1&&(a+=s);for(let n of t){let t=a+n,s=i.default.join(e,t);r!==s&&(0,o.copyFileSync)(r,s)}t.some(t=>r===i.default.join(e,a+t))||(0,o.unlinkSync)(r)}let s=[...new Set(r.map(e=>i.default.dirname(e)))].sort().reverse(),c=e=>{if(!((0,o.existsSync)(e)?(0,o.readdirSync)(e):[1]).length){(0,o.rmdirSync)(e);let t=e.split(i.default.sep).slice(0,-1);t.length&&c(t.join(i.default.sep))}};for(let t of s)t&&t!==`.`&&c(i.default.join(e,t));let l=t.flatMap(t=>a.sync(`**/*`+t,{cwd:e,dot:!0}));for(let e of l)n.logYellow(`final file path -----> `+e)}}}function T(e){let t=[``,`.ts`,`.tsx`,`.js`,`.jsx`];return e.reduce((e,[n,r,a=y])=>{let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_rolldown/runtime.js`),t=require(`../packages/utils/path.js`);require(`../packages/utils/index.js`);const n=require(`../packages/common/log/index.js`),r=require(`./state.js`);let i=require(`path`);i=e.__toESM(i);let a=require(`glob`);a=e.__toESM(a);let o=require(`fs`),s=require(`vite`),c=require(`@tailwindcss/vite`);c=e.__toESM(c);let l=require(`module`),u=require(`url`);var d=typeof __filename==`string`?__filename:(0,u.fileURLToPath)({}.url),f=i.default.dirname(d),p=process.cwd(),m=(0,l.createRequire)(d),h=i.default.resolve(f,`../../`),g=i.default.resolve(f,`../`),_=i.default.resolve(h,`./fe/frontend`),v=i.default.resolve(p,`.`),y=i.default.resolve(v,`./frontend`),b=e=>i.default.dirname(m.resolve(`${e}/package.json`));function x(){n.logWhite(`frameFEPath: `+_),n.logWhite(`runFEPath: `+y);let e=[_,y].reduce((e,n)=>{let r=[`js`,`jsx`,`ts`,`tsx`].map(e=>t.resolve(n,`/**`,`/*.`+e)),i=a.sync(r).filter(e=>e.includes(`.entry.`)).filter(e=>!e.includes(`.d.ts`));return[...e,...i]},[]),r={};if(!e.length)return r;let s;for(let n=0;n<e.length;n++){let c=e[n],l=i.default.resolve(c,`../`),u=a.sync(t.resolve(l,`*.html`))[0];if(u){let e=i.default.basename(u,`.html`);r[e]=i.default.resolve(p,u)}else{s||=(0,o.readFileSync)(i.default.resolve(g,`./app/view/entry.tpl`),`utf-8`);let e=c.split(i.default.sep).at(-1),t=s.replace(`<!-- {{InsertScript}} -->`,`<script type="module" src="./${e}"><\/script>`),n=l.split(i.default.sep).at(-1),a=i.default.join(l,n+`.html`);(0,o.writeFileSync)(a,t),r[n]=a}}return r}function S(){let e=r.buildFESharedState.options.output??`frame`;return e!==`frame`&&n.logRed(`Please pay attention to the build output.`),e===`run`?i.default.join(v,`./app/public/dist`):typeof e==`function`?e():i.default.join(g,`./app/public/dist`)}var C=async e=>{let t=(await import(`chokidar`)).watch(y,{ignored:/(^|[\/\\])\../,persistent:!0,ignoreInitial:!0}),r=!1;t.on(`change`,async t=>{if(!r){r=!0,n.logYellow(`changed: ${t}`);try{await e?.()}catch(e){n.logRed(e)}finally{r=!1}}}),n.logYellow(`watching ${y}/...`)};function w(e=`.html`){let t=[...new Set(Array.isArray(e)?e:[e])];return{name:`flatten-html`,apply:`build`,closeBundle(){let e=S();if(!(0,o.existsSync)(e))return;let r=a.sync(`**/*.html`,{cwd:e,dot:!0});for(let n of r){let r=i.default.join(e,n),a=i.default.basename(n,`.html`),s=`.entry`;a.indexOf(s)===-1&&(a+=s);for(let n of t){let t=a+n,s=i.default.join(e,t);r!==s&&(0,o.copyFileSync)(r,s)}t.some(t=>r===i.default.join(e,a+t))||(0,o.unlinkSync)(r)}let s=[...new Set(r.map(e=>i.default.dirname(e)))].sort().reverse(),c=e=>{if(!((0,o.existsSync)(e)?(0,o.readdirSync)(e):[1]).length){(0,o.rmdirSync)(e);let t=e.split(i.default.sep).slice(0,-1);t.length&&c(t.join(i.default.sep))}};for(let t of s)t&&t!==`.`&&c(i.default.join(e,t));let l=t.flatMap(t=>a.sync(`**/*`+t,{cwd:e,dot:!0}));for(let e of l)n.logYellow(`final file path -----> `+e)}}}function T(e){let t=[``,`.ts`,`.tsx`,`.js`,`.jsx`];return e.reduce((e,[n,r,a=y,s=`./defaultAlias`])=>{let c=t.map(e=>i.default.resolve(a,r+e)).find(e=>(0,o.existsSync)(e)),l=i.default.resolve(f,s);return c&&(l=c),e[n]=l,e},{})}var E=(...e)=>new RegExp(e.join(`[\\/]`)),D=e=>`_${e}_tcC`,O=async(e,t)=>{try{await(0,s.build)({configFile:!1,base:`/dist`,plugins:[(await import(`@vitejs/plugin-react`)).default(),w([`.tpl`,`.html`]),(0,c.default)()],build:{outDir:S(),emptyOutDir:!0,sourcemap:t===`dev`?`inline`:`hidden`,rolldownOptions:{input:e,output:{codeSplitting:{groups:[{name:D(`__nm`),test:E(`node_modules`),maxSize:5e5,minShareCount:2,priority:1},{name:D(`r`),test:E(`node_modules`,`react`),minShareCount:2,maxSize:5e5,priority:5},{name:D(`r-d`),test:E(`node_modules`,`react-dom`),minShareCount:2,maxSize:2e5,priority:10},{name:D(`r-r-d`),test:E(`node_modules`,`react-router-dom`),minShareCount:2,maxSize:5e5,priority:10},{name:D(`ajv`),test:E(`node_modules`,`ajv`),minShareCount:2,maxSize:2e5,priority:10},{name:D(`ui-lib`),test:E(`components`),minShareCount:2,maxSize:2e5,priority:5},{name:D(`ui-f-lib`),test:E(`components`,`.*?[Ff]orm.*?`),minShareCount:2,maxSize:5e4,priority:10}]}}}},resolve:{alias:{react:b(`react`),"react-dom":b(`react-dom`),"@tc/ui-react/hooks":i.default.resolve(f,`../../fe/packages/react/hooks/`),"@tc/ui-react":i.default.resolve(f,`../../fe/packages/react/ui/`),...T([[`@tc/scalability/SchemaForm/frameData`,`./extended/SchemaForm/data`,_],[`@tc/scalability/SchemaForm/data`,`./extended/SchemaForm/data`],[`@tc/scalability/SchemaPage/CallCom/data`,`./extended/SchemaPage/CallCom/data`],[`@tc/scalability/dash/customRoutes`,`./extended/dash/customRoutes`],[`@tc/scalability/dash/components`,`./extended/dash/components`],[`@tc/scalability/dash/routeGuard`,`./extended/dash/routeGuard`,void 0,`./defaultRouteGuard`]])}}}),n.logJoinColorized(n.colorize(`builded`,`green`),n.colorize(` -> `,`white`),n.colorize(`time: `,`cyan`),n.colorize(new Date,`pink`))}catch(e){if(n.logRed(e),t!==`dev`)throw e}};exports.VBuildFE=O,exports.entries=x,exports.outDir=S,exports.watchFiles=C;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`../../../_virtual/_rolldown/runtime.js`),t=require(`../paths.js`),n=require(`../../utils/path.js`),r=require(`../../utils/loadFile.js`),i=require(`../../utils/runFileFn.js`);require(`../../utils/index.js`);let a=require(`fs`),o=require(`koa-router`);o=e.__toESM(o,1);var s=e=>{let s,c=t.getAppPath(e,`business`);if(c)for(let t of[`router-guard.ts`,`router-guard.js`]){let r=n.resolve(c,t);if((0,a.existsSync)(r)){s=i.runFileFn(r)(e);break}}s||console.info(`-- [router-guard] -- not router-guard.(ts|js) file`);let l=[];t.getAppPaths(e).forEach(t=>{r.loadFileFn(t,`router`,{preProcessingFileCallback:t=>{let r=new o.default;r.level=0,l.push(r),i.runFileFn(n.resolve(t))(e,r)}})});let u=new o.default;u.level=99,u.get(`(.*)`,async(t,n)=>{console.info(`\x1B[31m%s\x1B[0m`,`-- notFound path: ${t.path} --`);let r=s?.(t);t.status=302,r?t.redirect(r??`/`):t.path.includes(`${e.options.apiPrefix}/`)?(t.status=200,t.body={code:404,message:`${t.path} not found`}):(t.status=404,t.body=`notFound`)}),l.push(u),l.sort((e,t)=>(e.level??0)<(t.level??0)?-1:1),l.forEach(t=>{e.use(t.routes()),e.use(t.allowedMethods())}),e.currentUseRouter=l};module.exports=s;
|
|
1
|
+
const e=require(`../../../_virtual/_rolldown/runtime.js`),t=require(`../paths.js`),n=require(`../../utils/path.js`),r=require(`../../utils/loadFile.js`),i=require(`../../utils/runFileFn.js`);require(`../../utils/index.js`);let a=require(`fs`),o=require(`koa-router`);o=e.__toESM(o,1);var s=e=>{let s,c=t.getAppPath(e,`business`);if(c)for(let t of[`router-guard.ts`,`router-guard.js`]){let r=n.resolve(c,t);if((0,a.existsSync)(r)){s=i.runFileFn(r)(e);break}}s||console.info(`-- [router-guard] -- not router-guard.(ts|js) file`);let l=[];t.getAppPaths(e).forEach(t=>{r.loadFileFn(t,`router`,{preProcessingFileCallback:t=>{let r=new o.default;r.level=0,l.push(r),i.runFileFn(n.resolve(t))(e,r)}})});let u=new o.default;u.level=99,u.get(`(.*)`,async(t,n)=>{console.info(`\x1B[31m%s\x1B[0m`,`-- notFound path: ${t.path} --`);let r=s?.(t);t.status=302,r?typeof r==`string`?t.redirect(r??`/`):(t.status=r.status,t.body=r.body):t.path.includes(`${e.options.apiPrefix}/`)?(t.status=200,t.body={code:404,message:`${t.path} not found`}):(t.status=404,t.body=`notFound`)}),l.push(u),l.sort((e,t)=>(e.level??0)<(t.level??0)?-1:1),l.forEach(t=>{e.use(t.routes()),e.use(t.allowedMethods())}),e.currentUseRouter=l};module.exports=s;
|
package/esm/bundler/utils.js
CHANGED
|
@@ -105,9 +105,9 @@ function P(e) {
|
|
|
105
105
|
".js",
|
|
106
106
|
".jsx"
|
|
107
107
|
];
|
|
108
|
-
return e.reduce((e, [n, r, i = O]) => {
|
|
109
|
-
let
|
|
110
|
-
return
|
|
108
|
+
return e.reduce((e, [n, r, i = O, a = "./defaultAlias"]) => {
|
|
109
|
+
let o = t.map((e) => s.resolve(i, r + e)).find((e) => u(e)), c = s.resolve(x, a);
|
|
110
|
+
return o && (c = o), e[n] = c, e;
|
|
111
111
|
}, {});
|
|
112
112
|
}
|
|
113
113
|
var F = (...e) => new RegExp(e.join("[\\/]")), I = (e) => `_${e}_tcC`, L = async (e, i) => {
|
|
@@ -193,7 +193,13 @@ var F = (...e) => new RegExp(e.join("[\\/]")), I = (e) => `_${e}_tcC`, L = async
|
|
|
193
193
|
["@tc/scalability/SchemaForm/data", "./extended/SchemaForm/data"],
|
|
194
194
|
["@tc/scalability/SchemaPage/CallCom/data", "./extended/SchemaPage/CallCom/data"],
|
|
195
195
|
["@tc/scalability/dash/customRoutes", "./extended/dash/customRoutes"],
|
|
196
|
-
["@tc/scalability/dash/components", "./extended/dash/components"]
|
|
196
|
+
["@tc/scalability/dash/components", "./extended/dash/components"],
|
|
197
|
+
[
|
|
198
|
+
"@tc/scalability/dash/routeGuard",
|
|
199
|
+
"./extended/dash/routeGuard",
|
|
200
|
+
void 0,
|
|
201
|
+
"./defaultRouteGuard"
|
|
202
|
+
]
|
|
197
203
|
])
|
|
198
204
|
} }
|
|
199
205
|
}), n(t("builded", "green"), t(" -> ", "white"), t("time: ", "cyan"), t(/* @__PURE__ */ new Date(), "pink"));
|
|
@@ -27,7 +27,7 @@ var s = (s) => {
|
|
|
27
27
|
d.level = 99, d.get("(.*)", async (e, t) => {
|
|
28
28
|
console.info("\x1B[31m%s\x1B[0m", `-- notFound path: ${e.path} --`);
|
|
29
29
|
let n = c?.(e);
|
|
30
|
-
e.status = 302, n ? e.redirect(n ?? "/") : e.path.includes(`${s.options.apiPrefix}/`) ? (e.status = 200, e.body = {
|
|
30
|
+
e.status = 302, n ? typeof n == "string" ? e.redirect(n ?? "/") : (e.status = n.status, e.body = n.body) : e.path.includes(`${s.options.apiPrefix}/`) ? (e.status = 200, e.body = {
|
|
31
31
|
code: 404,
|
|
32
32
|
message: `${e.path} not found`
|
|
33
33
|
}) : (e.status = 404, e.body = "notFound");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getText } from "../../src/common/language.js";
|
|
2
2
|
import { generateMenuItemData } from "../../src/common/generateMenuData.js";
|
|
3
|
+
import { getAuthTokenSync } from "../../src/common/auth/index.js";
|
|
3
4
|
import defaultAlias_default from "../../../bundler/defaultAlias.js";
|
|
4
5
|
import { Menu } from "../../../packages/react/ui/components/Menu/Menu.js";
|
|
5
6
|
import "../../../packages/react/ui/components/index.js";
|
|
@@ -7,6 +8,7 @@ import { useModeStore } from "../../src/stores/mode.js";
|
|
|
7
8
|
import { findMenuItem, leftSidebarBasePath } from "../../src/common/menu.js";
|
|
8
9
|
import HeaderView from "../../src/components/BasePage/HeaderView.js";
|
|
9
10
|
import useRouterParams from "../../src/hooks/useRouterParams.js";
|
|
11
|
+
import defaultRouteGuard_default from "../../../bundler/defaultRouteGuard.js";
|
|
10
12
|
import useInit from "../../../packages/react/hooks/useInit.js";
|
|
11
13
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
12
14
|
import { Outlet, useNavigate } from "react-router-dom";
|
|
@@ -110,6 +112,25 @@ var Dashboard = () => {
|
|
|
110
112
|
useEffect(() => {
|
|
111
113
|
if (initData && projectInfo) {
|
|
112
114
|
if (isInit.current) return;
|
|
115
|
+
if (typeof defaultRouteGuard_default === "function") {
|
|
116
|
+
const authToken = getAuthTokenSync();
|
|
117
|
+
const loginUrl = defaultRouteGuard_default({
|
|
118
|
+
projectInfo,
|
|
119
|
+
params,
|
|
120
|
+
pathname: location.pathname,
|
|
121
|
+
search: location.search,
|
|
122
|
+
hash: location.hash,
|
|
123
|
+
authToken,
|
|
124
|
+
isLoggedIn: Boolean(authToken)
|
|
125
|
+
});
|
|
126
|
+
if (typeof loginUrl === "string") {
|
|
127
|
+
isInit.current = true;
|
|
128
|
+
window.location.href = loginUrl;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
isInit.current = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
113
134
|
const homePage = getRenderableHomePage(projectInfo);
|
|
114
135
|
if (homePage) {
|
|
115
136
|
homePage.selectedKey && uSK(homePage.selectedKey);
|
|
@@ -12,3 +12,14 @@ export type DashHeaderUserAreaProps = {
|
|
|
12
12
|
export type DashComponentsMap = {
|
|
13
13
|
'HeaderView.userArea'?: ComponentType<DashHeaderUserAreaProps> | LazyExoticComponent<ComponentType<DashHeaderUserAreaProps>>;
|
|
14
14
|
};
|
|
15
|
+
export type DashRouteGuardContext = {
|
|
16
|
+
projectInfo: ProjectInfo;
|
|
17
|
+
params: Record<string, string | undefined>;
|
|
18
|
+
pathname: string;
|
|
19
|
+
search: string;
|
|
20
|
+
hash: string;
|
|
21
|
+
authToken: string | null;
|
|
22
|
+
isLoggedIn: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type DashRouteGuardResult = string | undefined;
|
|
25
|
+
export type DashRouteGuard = (context: DashRouteGuardContext) => DashRouteGuardResult;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const AUTH_HEADER_KEY = "Authorization";
|
|
2
|
+
export declare function getAuthTokenSync(): string | null;
|
|
2
3
|
export declare function getAuthToken(): Promise<string | null>;
|
|
3
4
|
export declare function clearAuthToken(): Promise<void>;
|
|
4
5
|
export declare const API_AUTH_HEADER_KEY = "projk";
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
var AUTH_LOCAL_KEY = "auth_token";
|
|
3
3
|
var API_AUTH_LOCAL_KEY = "p_J_k";
|
|
4
4
|
var AUTH_HEADER_KEY = "Authorization";
|
|
5
|
-
|
|
5
|
+
function getAuthTokenSync() {
|
|
6
6
|
if (typeof localStorage === "undefined") return null;
|
|
7
7
|
return localStorage.getItem(AUTH_LOCAL_KEY);
|
|
8
8
|
}
|
|
9
|
+
async function getAuthToken() {
|
|
10
|
+
return getAuthTokenSync();
|
|
11
|
+
}
|
|
9
12
|
async function clearAuthToken() {
|
|
10
13
|
if (typeof localStorage === "undefined") return;
|
|
11
14
|
localStorage.removeItem(AUTH_LOCAL_KEY);
|
|
@@ -18,4 +21,4 @@ var setApiAuth = (v) => {
|
|
|
18
21
|
return localStorage.setItem(API_AUTH_LOCAL_KEY, v);
|
|
19
22
|
};
|
|
20
23
|
//#endregion
|
|
21
|
-
export { API_AUTH_HEADER_KEY, AUTH_HEADER_KEY, clearAuthToken, getApiAuth, getAuthToken, setApiAuth };
|
|
24
|
+
export { API_AUTH_HEADER_KEY, AUTH_HEADER_KEY, clearAuthToken, getApiAuth, getAuthToken, getAuthTokenSync, setApiAuth };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { frontendLangKeys } from "../language/index.js";
|
|
2
2
|
import { getText } from "./language.js";
|
|
3
|
+
import { API_AUTH_HEADER_KEY, AUTH_HEADER_KEY, clearAuthToken, getApiAuth, getAuthToken } from "./auth/index.js";
|
|
3
4
|
import { FreezeState, apiFreezerStore } from "../stores/apiFreezer.js";
|
|
4
5
|
import FetchAxios from "../../../packages/common/http/index.js";
|
|
5
|
-
import { API_AUTH_HEADER_KEY, AUTH_HEADER_KEY, clearAuthToken, getApiAuth, getAuthToken } from "./auth/index.js";
|
|
6
6
|
import md5 from "md5";
|
|
7
7
|
//#region frontend/src/common/request.ts
|
|
8
8
|
var BASE_URL = "/api";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const AUTH_HEADER_KEY = "Authorization";
|
|
2
|
+
export declare function getAuthTokenSync(): string | null;
|
|
2
3
|
export declare function getAuthToken(): Promise<string | null>;
|
|
3
4
|
export declare function clearAuthToken(): Promise<void>;
|
|
4
5
|
export declare const API_AUTH_HEADER_KEY = "projk";
|
package/package.json
CHANGED