@qlover/create-app 0.9.0 → 0.10.1

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/.env.template +9 -10
  5. package/dist/templates/next-app/eslint.config.mjs +5 -0
  6. package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
  7. package/dist/templates/next-app/next.config.ts +1 -1
  8. package/dist/templates/next-app/package.json +2 -3
  9. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  10. package/dist/templates/next-app/src/app/[locale]/page.tsx +1 -1
  11. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  12. package/dist/templates/next-app/src/app/api/locales/json/route.ts +2 -1
  13. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
  14. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
  15. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
  16. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
  17. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
  18. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
  19. package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
  20. package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
  21. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
  22. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
  23. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +4 -3
  24. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -3
  25. package/dist/templates/next-app/src/core/globals.ts +2 -1
  26. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +4 -3
  27. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +5 -6
  28. package/dist/templates/next-app/src/i18n/request.ts +2 -2
  29. package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
  30. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
  31. package/dist/templates/react-app/__tests__/__mocks__/{MockAppConfit.ts → MockAppConfig.ts} +1 -1
  32. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +10 -17
  33. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +27 -8
  34. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -1
  35. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +110 -0
  36. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +3 -2
  37. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +13 -0
  38. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +3 -1
  39. package/dist/templates/react-app/config/IOCIdentifier.ts +9 -6
  40. package/dist/templates/react-app/config/common.ts +38 -0
  41. package/dist/templates/react-app/config/feapi.mock.json +5 -12
  42. package/dist/templates/react-app/eslint.config.mjs +6 -5
  43. package/dist/templates/react-app/package.json +1 -1
  44. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +22 -13
  45. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +3 -3
  46. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +17 -12
  47. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +19 -2
  48. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +2 -4
  49. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +15 -9
  50. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +55 -0
  51. package/dist/templates/react-app/src/base/services/I18nService.ts +1 -0
  52. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +43 -0
  53. package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +16 -0
  54. package/dist/templates/react-app/src/base/services/UserService.ts +51 -80
  55. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +8 -3
  56. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +6 -6
  57. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +28 -0
  58. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +24 -18
  59. package/dist/templates/react-app/src/core/globals.ts +10 -11
  60. package/dist/templates/react-app/src/main.tsx +1 -1
  61. package/dist/templates/react-app/src/pages/auth/Layout.tsx +4 -4
  62. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +1 -1
  63. package/dist/templates/react-app/src/pages/base/Layout.tsx +3 -3
  64. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +44 -0
  65. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +1 -3
  66. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +9 -0
  67. package/dist/templates/react-app/src/uikit/hooks/{useI18nGuard.ts → useRouterI18nGuard.ts} +7 -4
  68. package/dist/templates/react-app/tsconfig.app.json +1 -1
  69. package/package.json +3 -3
  70. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +0 -102
  71. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +0 -61
  72. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +0 -57
  73. package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +0 -28
  74. package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +0 -16
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 0.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ ## 0.10.0
8
+
9
+ ### Minor Changes
10
+
11
+ #### ✨ Features
12
+
13
+ - **react-app:** Update TypeScript configurations and enhance user services ([6b33d18](https://github.com/qlover/fe-base/commit/6b33d18bdec4d0943a615d746e929348ea965031)) ([#545](https://github.com/qlover/fe-base/pull/545))
14
+ - Added path mappings for corekit-bridge and fe-corekit in TypeScript configuration files.
15
+ - Refactored integration tests to use updated import paths and improved error handling.
16
+ - Introduced a new BaseLayoutService for managing user authentication and layout rendering.
17
+ - Enhanced UserService with credential management and user information validation.
18
+ - Removed obsolete ProcesserExecutor and UserAuthProvider components to streamline the codebase.
19
+ - Updated mock API configurations for improved endpoint handling and response structures.
20
+ - Added new hooks for router internationalization and layout management.
21
+
3
22
  ## 0.9.0
4
23
 
5
24
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${t}`,_n=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),mn=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Ye(D,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#D.frames.length,this.#c=t);let{frames:r}=this.#D,u=r[this.#i];this.color&&(u=d[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",D=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+D+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#n;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#n=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&$e.hide(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&(this.#r=!0,Je.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&$e.show(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&this.#r&&(Je.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:H.success,text:t})}fail(t){return this.stopAndPersist({symbol:H.error,text:t})}warn(t){return this.stopAndPersist({symbol:H.warning,text:t})}info(t){return this.stopAndPersist({symbol:H.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function rr(e){return new Ze(e)}async function ur(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:D}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=rr(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}var bt=require("fs");var P=require("path"),me=require("fs"),fr=h(cr(),1),Dt=require("fs");var de=require("fs"),X=class{static ensureDir(t){(0,de.existsSync)(t)||(0,de.mkdirSync)(t,{recursive:!0})}};var{copyFile:us,stat:is}=Dt.promises,_e=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=(0,P.join)(t,this.ignoreFile);if(!(0,me.existsSync)(r))return;let D=(0,me.readFileSync)(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.9.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Wl(){let e=new YD.Command;return e.version(vt.version,"-v, --version","Show version").description(vt.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function VD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Wl(),i=(0,xt.resolve)(e,"./templates"),D=(0,xt.resolve)(e,"./configs");(0,yt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,yt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}VD(__dirname).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.10.1",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Wl(){let e=new YD.Command;return e.version(vt.version,"-v, --version","Show version").description(vt.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function VD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Wl(),i=(0,xt.resolve)(e,"./templates"),D=(0,xt.resolve)(e,"./configs");(0,yt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,yt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}VD(__dirname).catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,pD=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),hD=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(n,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#n.frames.length,this.#c=t);let{frames:r}=this.#n,u=r[this.#i];this.color&&(u=E[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",n=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+n+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#D;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#D=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#D=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&je.hide(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&(this.#r=!0,We.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&je.show(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&this.#r&&(We.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:k.success,text:t})}fail(t){return this.stopAndPersist({symbol:k.error,text:t})}warn(t){return this.stopAndPersist({symbol:k.warning,text:t})}info(t){return this.stopAndPersist({symbol:k.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",n=t.text??this.text,s=typeof n=="string"?(i?" ":"")+n:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function zt(e){return new He(e)}async function Kt(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:n}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=zt(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(n===void 0?void 0:typeof n=="string"?n:n(s)),s}}import{existsSync as Nn}from"fs";var nr=ue(ir(),1);import{dirname as QD,join as Ze}from"path";import{existsSync as es,readFileSync as ts}from"fs";import{promises as Dr}from"fs";import{existsSync as JD,mkdirSync as ZD}from"fs";var H=class{static ensureDir(t){JD(t)||ZD(t,{recursive:!0})}};var{copyFile:rs,stat:us}=Dr,Ce=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=Ze(t,this.ignoreFile);if(!es(r))return;let n=ts(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.9.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.10.1",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
@@ -1,26 +1,25 @@
1
1
  # ci
2
2
  NPM_TOKEN=
3
3
  GITHUB_TOKEN=
4
-
5
4
  # fe-scripts
6
5
  FE_RELEASE_BRANCH=master
7
6
  FE_RELEASE=false
8
7
  FE_RELEASE_ENV=localhost
9
-
10
- # ======== server
11
- APP_HOST=http://localhost:3100
8
+ # ===== server
9
+ APP_HOST=
12
10
  SUPABASE_URL=
13
11
  SUPABASE_ANON_KEY=
14
12
  JWT_SECRET=
15
-
16
- # browser
13
+ CEREBRAS_API_KEY=
14
+ CEREBRAS_BASE_URL=
15
+ # ===== browser
17
16
  NEXT_PUBLIC_USER_TOKEN_STORAGE_KEY=fe_user_token
18
17
  NEXT_PUBLIC_OPEN_AI_MODELS='["gpt-4o-mini","gpt-3.5-turbo","gpt-3.5-turbo-2","gpt-4","gpt-4-32k"]'
19
18
  NEXT_PUBLIC_OPEN_AI_BASE_URL=
20
19
  NEXT_PUBLIC_OPEN_AI_TOKEN=sk-proj-1234567890
21
20
  NEXT_PUBLIC_OPEN_AI_TOKEN_PREFIX=Bearer
22
21
  NEXT_PUBLIC_OPEN_AI_REQUIRE_TOKEN=true
23
- NEXT_PUBLIC_LOGIN_USER=admin
24
- NEXT_PUBLIC_LOGIN_PASSWORD=prod-123456
25
- NEXT_PUBLIC_FE_API_BASE_URL=https://feapi.example.com/
26
- NEXT_PUBLIC_STRING_ENCRYPT_KEY=YourSecretKey123!@#
22
+ NEXT_PUBLIC_LOGIN_USER=xxxx
23
+ NEXT_PUBLIC_LOGIN_PASSWORD=xxx
24
+ NEXT_PUBLIC_FE_API_BASE_URL=https://xxx.example.com/
25
+ NEXT_PUBLIC_STRING_ENCRYPT_KEY=xxx!@#
@@ -173,6 +173,11 @@ const eslintConfig = [
173
173
  singleQuote: true,
174
174
  trailingComma: 'none',
175
175
  endOfLine: 'lf'
176
+ },
177
+ {
178
+ // 仅用于单独部署时对 eslint prettier 插件自动查找 prettierrc 时报错
179
+ // 注意: vscode 等编辑器会失效, 作为单独项目开发时可以去掉
180
+ usePrettierrc: false
176
181
  }
177
182
  ],
178
183
  // 默认禁用 export default
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import type { SafeParseReturnType } from 'zod';
2
3
 
3
4
  export const UserRole = {
4
5
  ADMIN: 0,
@@ -16,9 +17,22 @@ export const userSchema = z.object({
16
17
  * 加密的token, 包含token, 过期时间
17
18
  */
18
19
  credential_token: z.string(),
19
- email_confirmed_at: z.number(),
20
- created_at: z.number(),
21
- updated_at: z.number()
20
+ email_confirmed_at: z.number().nullable().optional(),
21
+ created_at: z.string(),
22
+ updated_at: z.string().nullable().optional()
22
23
  });
23
24
 
24
25
  export type UserSchema = z.infer<typeof userSchema>;
26
+
27
+ export type UserCredential = {
28
+ credential_token: string;
29
+ };
30
+
31
+ export function isWebUserSchema(
32
+ value: unknown
33
+ ): SafeParseReturnType<
34
+ Omit<UserSchema, 'password'>,
35
+ Omit<UserSchema, 'password'>
36
+ > {
37
+ return userSchema.omit({ password: true }).safeParse(value);
38
+ }
@@ -2,7 +2,7 @@ import createNextIntlPlugin from 'next-intl/plugin';
2
2
  import { generateLocales } from './make/generateLocales';
3
3
  import type { NextConfig } from 'next';
4
4
 
5
- const withNextIntl = createNextIntlPlugin();
5
+ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
6
6
 
7
7
  // 在构建开始时生成本地化文件
8
8
  generateLocales().catch((error) => {
@@ -24,10 +24,9 @@
24
24
  "@brain-toolkit/antd-theme-override": "^0.0.3",
25
25
  "@brain-toolkit/bridge": "^0.0.1",
26
26
  "@brain-toolkit/react-kit": "^0.1.0",
27
- "@qlover/corekit-bridge": "^1.6.5",
28
- "@qlover/fe-corekit": "^2.1.0",
27
+ "@qlover/corekit-bridge": "latest",
28
+ "@qlover/fe-corekit": "latest",
29
29
  "@qlover/slice-store-react": "^1.4.1",
30
- "@supabase/auth-helpers-nextjs": "^0.10.0",
31
30
  "@supabase/ssr": "^0.7.0",
32
31
  "@supabase/supabase-js": "^2.57.2",
33
32
  "@types/jsonwebtoken": "^9.0.10",
@@ -17,7 +17,7 @@ export async function generateStaticParams() {
17
17
  }
18
18
 
19
19
  // Allow Next.js to statically generate this page if possible (default behavior)
20
- export const dynamic = 'auto'; // Enable static generation when possible, fallback to dynamic if needed
20
+ // Note: 'auto' is not a valid value in Next.js 15, removed to use default behavior
21
21
 
22
22
  // Optional: Use revalidate if you want ISR (Incremental Static Regeneration)
23
23
  // export const revalidate = 3600; // Rebuild every hour (optional)
@@ -23,7 +23,7 @@ export async function generateStaticParams() {
23
23
  }
24
24
 
25
25
  // Allow Next.js to statically generate this page if possible (default behavior)
26
- export const dynamic = 'auto'; // Enable static generation when possible, fallback to dynamic if needed
26
+ // Note: 'auto' is not a valid value in Next.js 15, removed to use default behavior
27
27
 
28
28
  // Optional: Use revalidate if you want ISR (Incremental Static Regeneration)
29
29
  // export const revalidate = 3600; // Rebuild every hour (optional)
@@ -17,7 +17,7 @@ export async function generateStaticParams() {
17
17
  }
18
18
 
19
19
  // Allow Next.js to statically generate this page if possible (default behavior)
20
- export const dynamic = 'auto'; // Enable static generation when possible, fallback to dynamic if needed
20
+ // Note: 'auto' is not a valid value in Next.js 15, removed to use default behavior
21
21
 
22
22
  // Optional: Use revalidate if you want ISR (Incremental Static Regeneration)
23
23
  // export const revalidate = 3600; // Rebuild every hour (optional)
@@ -5,7 +5,8 @@ import { ApiLocaleService } from '@/server/services/ApiLocaleService';
5
5
  import { i18nConfig } from '@config/i18n';
6
6
  import type { LocaleType } from '@config/i18n';
7
7
 
8
- export const revalidate = i18nConfig.localeCacheTime;
8
+ // Use literal value instead of imported config to ensure static analysis
9
+ export const revalidate = 60; // Cache time in seconds (matches i18nConfig.localeCacheTime)
9
10
 
10
11
  export async function GET(req: NextRequest) {
11
12
  const searchParams = Object.fromEntries(req.nextUrl.searchParams.entries());
@@ -11,8 +11,7 @@ import type {
11
11
  import type { ModalFuncProps } from 'antd';
12
12
 
13
13
  export interface DialogHandlerOptions
14
- extends NotificationOptions,
15
- ModalFuncProps {
14
+ extends NotificationOptions, ModalFuncProps {
16
15
  content: string;
17
16
  }
18
17
 
@@ -6,8 +6,9 @@ import type {
6
6
  RequestAdapterConfig
7
7
  } from '@qlover/fe-corekit';
8
8
 
9
- export interface RequestEncryptPluginProps<Request = unknown>
10
- extends RequestAdapterConfig<Request> {
9
+ export interface RequestEncryptPluginProps<
10
+ Request = unknown
11
+ > extends RequestAdapterConfig<Request> {
11
12
  /**
12
13
  * 加密密码在 HTTP 请求中
13
14
  *
@@ -18,9 +19,7 @@ export interface RequestEncryptPluginProps<Request = unknown>
18
19
  encryptProps?: string[] | string;
19
20
  }
20
21
 
21
- export class RequestEncryptPlugin
22
- implements ExecutorPlugin<RequestEncryptPluginProps>
23
- {
22
+ export class RequestEncryptPlugin implements ExecutorPlugin<RequestEncryptPluginProps> {
24
23
  readonly pluginName = 'RequestEncryptPlugin';
25
24
 
26
25
  constructor(protected encryptor: Encryptor<string, string>) {}
@@ -1,48 +1,63 @@
1
1
  import { inject, injectable } from 'inversify';
2
- import type { UserSchema } from '@migrations/schema/UserSchema';
2
+ import { omit } from 'lodash';
3
+ import type { LoginValidatorData } from '@/server/validators/LoginValidator';
4
+ import {
5
+ isWebUserSchema,
6
+ type UserCredential,
7
+ type UserSchema
8
+ } from '@migrations/schema/UserSchema';
3
9
  import { AppUserApi } from '../services/appApi/AppUserApi';
4
- import type { AppApiSuccessInterface } from '../port/AppApiInterface';
5
10
  import type { AppUserApiInterface } from '../port/AppUserApiInterface';
6
- import type {
7
- LoginResponseData,
8
- UserAuthApiInterface,
9
- UserAuthStoreInterface
10
- } from '@qlover/corekit-bridge';
11
+ import type { UserServiceGateway } from '@qlover/corekit-bridge';
11
12
 
12
13
  @injectable()
13
- export class UserServiceApi implements UserAuthApiInterface<UserSchema> {
14
- protected store: UserAuthStoreInterface<UserSchema> | null = null;
15
-
14
+ export class UserServiceApi implements UserServiceGateway<
15
+ UserSchema,
16
+ UserCredential
17
+ > {
16
18
  constructor(@inject(AppUserApi) protected appUserApi: AppUserApiInterface) {}
17
19
 
18
- getStore(): UserAuthStoreInterface<UserSchema> | null {
19
- return this.store;
20
+ getUserInfo(_params?: unknown): Promise<UserSchema | null> {
21
+ if (_params && isWebUserSchema(_params).success) {
22
+ return Promise.resolve(omit(_params, 'credential_token') as UserSchema);
23
+ }
24
+
25
+ return Promise.resolve(null);
20
26
  }
21
- setStore(store: UserAuthStoreInterface<UserSchema>): void {
22
- this.store = store;
27
+
28
+ refreshUserInfo<Params>(
29
+ _params?: Params | undefined
30
+ ): Promise<UserSchema | null> {
31
+ return this.getUserInfo(_params);
23
32
  }
24
33
 
25
- async login(params: {
26
- email: string;
27
- password: string;
28
- }): Promise<LoginResponseData> {
34
+ async login(params: LoginValidatorData): Promise<UserCredential> {
29
35
  const response = await this.appUserApi.login(params);
30
- return (response as AppApiSuccessInterface).data as LoginResponseData;
36
+
37
+ if (!response.data.success) {
38
+ throw response;
39
+ }
40
+
41
+ return response.data.data as UserCredential;
31
42
  }
32
43
 
33
- async register(params: {
34
- email: string;
35
- password: string;
36
- }): Promise<LoginResponseData> {
44
+ async register(params: LoginValidatorData): Promise<UserSchema> {
37
45
  const response = await this.appUserApi.register(params);
38
- return (response as AppApiSuccessInterface).data as LoginResponseData;
39
- }
40
46
 
41
- async logout(): Promise<void> {
42
- await this.appUserApi.logout();
47
+ if (!response.data.success) {
48
+ throw response;
49
+ }
50
+
51
+ return response.data.data as UserSchema;
43
52
  }
44
53
 
45
- getUserInfo(loginData: LoginResponseData): Promise<UserSchema> {
46
- return Promise.resolve(loginData as unknown as UserSchema);
54
+ async logout<P = unknown, Result = void>(params?: P): Promise<Result> {
55
+ const response = await this.appUserApi.logout(params);
56
+
57
+ if (!response.data.success) {
58
+ throw response;
59
+ }
60
+
61
+ return response.data.data as Result;
47
62
  }
48
63
  }
@@ -33,8 +33,7 @@ export const ZodType2RenderFormMap = {
33
33
  export class ZodColumnBuilder<
34
34
  Value extends ZodRawShape,
35
35
  Input extends ZodObject<Value>
36
- > implements ZodBuilderInterface<Input, ResourceTableOption<z.infer<Input>>>
37
- {
36
+ > implements ZodBuilderInterface<Input, ResourceTableOption<z.infer<Input>>> {
38
37
  protected optionMap: OptionMap<Value, Input>;
39
38
 
40
39
  constructor(
@@ -1,15 +1,27 @@
1
- import type { AppApiResult } from './AppApiInterface';
1
+ import type { LoginValidatorData } from '@/server/validators/LoginValidator';
2
+ import type { UserSchema } from '@migrations/schema/UserSchema';
3
+ import type { AppApiTransaction } from '../services/appApi/AppApiRequester';
4
+
5
+ export type UserApiLoginTransaction = AppApiTransaction<
6
+ LoginValidatorData,
7
+ UserSchema
8
+ >;
9
+
10
+ export type UserApiRegisterTransaction = AppApiTransaction<
11
+ LoginValidatorData,
12
+ UserSchema
13
+ >;
14
+
15
+ export type UserApiLogoutTransaction = AppApiTransaction<unknown, void>;
2
16
 
3
17
  export interface AppUserApiInterface {
4
- login(params: {
5
- email: string;
6
- password: string;
7
- }): Promise<AppApiResult<unknown>>;
18
+ login(
19
+ params: UserApiLoginTransaction['data']
20
+ ): Promise<UserApiLoginTransaction['response']>;
8
21
 
9
- register(params: {
10
- email: string;
11
- password: string;
12
- }): Promise<AppApiResult<unknown>>;
22
+ register(
23
+ params: UserApiRegisterTransaction['data']
24
+ ): Promise<UserApiRegisterTransaction['response']>;
13
25
 
14
- logout(): Promise<AppApiResult<unknown>>;
26
+ logout(params?: unknown): Promise<UserApiLogoutTransaction['response']>;
15
27
  }
@@ -1,12 +1,20 @@
1
- import { UserAuthService } from '@qlover/corekit-bridge';
2
- import type { UserSchema } from '@migrations/schema/UserSchema';
3
- import type { ExecutorPlugin } from '@qlover/fe-corekit';
1
+ import type { UserCredential, UserSchema } from '@migrations/schema/UserSchema';
2
+ import type { UserService as CorekitBridgeUserServiceInterface } from '@qlover/corekit-bridge';
4
3
 
5
- export abstract class UserServiceInterface
6
- extends UserAuthService<UserSchema>
7
- implements ExecutorPlugin
8
- {
9
- readonly pluginName = 'UserService';
4
+ export interface UserServiceInterface extends CorekitBridgeUserServiceInterface<
5
+ UserSchema,
6
+ UserCredential
7
+ > {
8
+ // You can add your own methods here
10
9
 
11
- abstract getToken(): string | null;
10
+ /**
11
+ * Get the user token
12
+ *
13
+ * This is a extends method from the corekit-bridge UserServiceInterface.
14
+ */
15
+ getToken(): string;
16
+
17
+ isUserInfo(value: unknown): value is UserSchema;
18
+
19
+ isUserCredential(value: unknown): value is UserCredential;
12
20
  }
@@ -11,10 +11,9 @@ import type { PaginationInterface } from '@/server/port/PaginationInterface';
11
11
 
12
12
  export class ResourceService<
13
13
  T,
14
- Store extends
15
- ResourceStore<ResourceStateInterface> = ResourceStore<ResourceStateInterface>
16
- > implements ResourceServiceInterface<T>
17
- {
14
+ Store extends ResourceStore<ResourceStateInterface> =
15
+ ResourceStore<ResourceStateInterface>
16
+ > implements ResourceServiceInterface<T> {
18
17
  readonly unionKey: string = 'id';
19
18
 
20
19
  constructor(
@@ -1,24 +1,48 @@
1
+ import { UserService as CorekitBridgeUserService } from '@qlover/corekit-bridge';
1
2
  import { injectable, inject } from 'inversify';
2
- import type { UserSchema } from '@migrations/schema/UserSchema';
3
- import { AppConfig } from '../cases/AppConfig';
3
+ import { isObject, isString } from 'lodash';
4
+ import {
5
+ userSchema,
6
+ type UserCredential,
7
+ type UserSchema
8
+ } from '@migrations/schema/UserSchema';
4
9
  import { UserServiceApi } from '../cases/UserServiceApi';
5
- import { UserServiceInterface } from '../port/UserServiceInterface';
6
- import type { UserAuthApiInterface } from '@qlover/corekit-bridge';
10
+ import type { UserServiceInterface } from '../port/UserServiceInterface';
11
+ import type { UserServiceGateway } from '@qlover/corekit-bridge';
7
12
 
8
13
  @injectable()
9
- export class UserService extends UserServiceInterface {
14
+ export class UserService
15
+ extends CorekitBridgeUserService<UserSchema, UserCredential>
16
+ implements UserServiceInterface
17
+ {
10
18
  constructor(
11
- @inject(AppConfig) protected appConfig: AppConfig,
12
- @inject(UserServiceApi) protected userApi: UserAuthApiInterface<UserSchema>
19
+ @inject(UserServiceApi)
20
+ userApi: UserServiceGateway<UserSchema, UserCredential>
13
21
  ) {
14
- super(userApi, {
15
- credentialStorage: {
16
- key: appConfig.userTokenKey
17
- }
22
+ super({
23
+ gateway: userApi
24
+ // next-js ssr 将 credential 存储在 cookie 中无需存储用户信息到本地
25
+ // store: {
26
+ // storageKey: appConfig.userInfoKey,
27
+ // credentialStorageKey: appConfig.userTokenKey,
28
+ // persistUserInfo: true,
29
+ // }
18
30
  });
19
31
  }
20
32
 
21
- getToken(): string | null {
22
- return this.store.getCredential();
33
+ getToken(): string {
34
+ return this.store.getCredential()?.credential_token ?? '';
35
+ }
36
+
37
+ isUserInfo(value: unknown): value is UserSchema {
38
+ return userSchema.safeParse(value).success;
39
+ }
40
+
41
+ isUserCredential(value: unknown): value is UserCredential {
42
+ return (
43
+ isObject(value) &&
44
+ 'credential_token' in value &&
45
+ isString(value.credential_token)
46
+ );
23
47
  }
24
48
  }
@@ -13,8 +13,7 @@ import type {
13
13
  } from '@qlover/fe-corekit';
14
14
 
15
15
  export interface AppApiConfig<Request = unknown>
16
- extends RequestAdapterConfig<Request>,
17
- RequestEncryptPluginProps<Request> {}
16
+ extends RequestAdapterConfig<Request>, RequestEncryptPluginProps<Request> {}
18
17
 
19
18
  /**
20
19
  * UserApiResponse
@@ -33,11 +32,13 @@ export type AppApiResponse<
33
32
  /**
34
33
  * UserApi common transaction
35
34
  */
36
- export interface AppApiTransaction<Request = unknown, Response = unknown>
37
- extends RequestTransactionInterface<
38
- AppApiConfig<Request>,
39
- AppApiResponse<Request, Response>
40
- > {
35
+ export interface AppApiTransaction<
36
+ Request = unknown,
37
+ Response = unknown
38
+ > extends RequestTransactionInterface<
39
+ AppApiConfig<Request>,
40
+ AppApiResponse<Request, Response>
41
+ > {
41
42
  data: AppApiConfig<Request>['data'];
42
43
  }
43
44