@qlover/create-app 0.4.6 → 0.6.0

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 (28) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/react-app/config/Identifier/index.ts +1 -1
  5. package/dist/templates/react-app/config/feapi.mock.json +6 -0
  6. package/dist/templates/react-app/package.json +2 -2
  7. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +87 -6
  8. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +11 -0
  9. package/dist/templates/react-app/src/base/cases/AppConfig.ts +11 -0
  10. package/dist/templates/react-app/src/base/services/UserService.ts +61 -83
  11. package/dist/templates/react-app/src/core/IOC.ts +11 -6
  12. package/dist/templates/react-app/src/core/bootstrap.ts +2 -2
  13. package/dist/templates/react-app/src/core/globals.ts +23 -7
  14. package/dist/templates/react-app/src/core/registers/RegisterApi.ts +1 -1
  15. package/dist/templates/react-app/src/core/registers/RegisterCommon.ts +10 -11
  16. package/dist/templates/react-app/src/core/registers/RegisterControllers.ts +2 -2
  17. package/dist/templates/react-app/src/core/registers/RegisterGlobals.ts +12 -4
  18. package/dist/templates/react-app/src/pages/404.tsx +1 -1
  19. package/dist/templates/react-app/src/pages/auth/Layout.tsx +1 -1
  20. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +0 -2
  21. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +0 -2
  22. package/dist/templates/react-app/src/pages/base/ErrorIdentifierPage.tsx +1 -1
  23. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +1 -1
  24. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +1 -1
  25. package/dist/templates/react-app/src/uikit/controllers/JSONStorageController.ts +3 -3
  26. package/dist/templates/react-app/src/uikit/providers/UserAuthProvider.tsx +1 -1
  27. package/package.json +1 -1
  28. /package/dist/templates/react-app/config/Identifier/{Error.ts → common.error.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ #### 🐞 Bug Fixes
8
+
9
+ - **react-app:** update package dependencies and refactor UserApi and UserService ([35e58e4](https://github.com/qlover/fe-base/commit/35e58e4e618082b5dee16351d7d78bbd54ca9e83)) ([#460](https://github.com/qlover/fe-base/pull/460))
10
+ - Changed dependencies in package.json to use the latest versions of @qlover/corekit-bridge and @qlover/fe-corekit for improved stability.
11
+ - Refactored UserApi to utilize UserAuthState for better state management and type safety.
12
+ - Updated UserService to align with the new UserApi structure, enhancing type definitions and simplifying state handling.
13
+ - Removed outdated comments and streamlined the code for better readability and maintainability.
14
+
15
+ These changes aim to enhance the overall functionality and reliability of the user authentication system within the react-app template.
16
+
17
+ ## 0.5.0
18
+
19
+ ### Minor Changes
20
+
21
+ #### ✨ Features
22
+
23
+ - **react-app:** enhance user authentication and error handling ([ee00e24](https://github.com/qlover/fe-base/commit/ee00e24ce1c713aff91100ff30f9c84d8b523c80)) ([#458](https://github.com/qlover/fe-base/pull/458))
24
+ - Updated package.json to reference local corekit dependencies for improved development.
25
+ - Added new error identifiers in common.error.ts for better error management.
26
+ - Refactored UserApi to implement registration and improved login handling with token validation.
27
+ - Introduced common error handling in UserService and updated related components to utilize new error identifiers.
28
+ - Enhanced storage management by integrating new storage interfaces and updating related services.
29
+
30
+ These changes aim to streamline user authentication processes and improve error reporting across the application.
31
+
3
32
  ## 0.4.6
4
33
 
5
34
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${t}`,dn=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),_n=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,f=this.#g(a," "),p=u+i+s+f+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function tr(e){return new Ze(e)}async function rr(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=tr(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 HD=require("fs");var P=require("path"),me=require("fs"),cr=h(lr(),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:rs,stat:us}=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,cr.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 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}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),kD=h(LD(),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,kD.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 $D=["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,HD.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.FeScriptContext(t),this.subPackages=["node-lib","react-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 WD.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 $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:(0,v.join)(D,u),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 bt={name:"@qlover/create-app",version:"0.4.6",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:{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 Ul(){let e=new YD.Command;return e.version(bt.version,"-v, --version","Show version").description(bt.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 Wl(){let{dryRun:e,verbose:t,...r}=Ul(),u=(0,vt.resolve)("./templates"),i=(0,vt.resolve)("./configs");(0,xt.existsSync)(u)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(i)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Wl().catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,cr.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 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}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),kD=h(LD(),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,kD.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 $D=["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,HD.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.FeScriptContext(t),this.subPackages=["node-lib","react-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 WD.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 $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:(0,v.join)(D,u),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 bt={name:"@qlover/create-app",version:"0.6.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:{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 Ul(){let e=new YD.Command;return e.version(bt.version,"-v, --version","Show version").description(bt.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 Wl(){let{dryRun:e,verbose:t,...r}=Ul(),u=(0,vt.resolve)("./templates"),i=(0,vt.resolve)("./configs");(0,xt.existsSync)(u)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(i)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Wl().catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,cn=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),fn=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(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=E[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&&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??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,f=this.#g(a," "),p=u+i+s+f+`
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:D}=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(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}import{existsSync as Hl}from"fs";var Dr=ue(ir(),1);import{dirname as Jn,join as Ze}from"path";import{existsSync as Zn,readFileSync as Qn}from"fs";import{promises as nr}from"fs";import{existsSync as Kn,mkdirSync as Xn}from"fs";var H=class{static ensureDir(t){Kn(t)||Xn(t,{recursive:!0})}};var{copyFile:es,stat:ts}=nr,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(!Zn(r))return;let D=Qn(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(D)}async copyFiles(t,r,u,i){let D=await nr.readdir(t);await Promise.all(D.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(Jn(a)),(await ts(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await es(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var ID=ue(qD(),1);import{readFileSync as Ll,writeFileSync as kl,existsSync as $l}from"fs";var me=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 Ll(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){kl(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,ID.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 $l(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var jD=["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(!Hl(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Ul(t),this.subPackages=["node-lib","react-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new me}get logger(){return this.context.logger}async steps(t){try{return await Wl.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 jD.includes(t)}async getGeneratorContext(){let t=_t(this.subPackages,jD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=mt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:N(D,u),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:D}=this.context.options;for(let o of u){let s=N(D,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.4.6",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:{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 zl(){let e=new Vl;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 Kl(){let{dryRun:e,verbose:t,...r}=zl(),u=ND("./templates"),i=ND("./configs");GD(u)||(console.error("Template is empty!"),process.exit(1)),GD(i)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Kl().catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(D)}async copyFiles(t,r,u,i){let D=await nr.readdir(t);await Promise.all(D.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(Jn(a)),(await ts(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await es(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var ID=ue(qD(),1);import{readFileSync as Ll,writeFileSync as kl,existsSync as $l}from"fs";var me=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 Ll(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){kl(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,ID.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 $l(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var jD=["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(!Hl(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Ul(t),this.subPackages=["node-lib","react-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new me}get logger(){return this.context.logger}async steps(t){try{return await Wl.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 jD.includes(t)}async getGeneratorContext(){let t=_t(this.subPackages,jD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=mt(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=(s,a)=>(this.logger.debug("copyCallback",s,a),this.compose.composeConfigFile(t,s,a)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}await this.copyer.copyPaths({sourcePath:N(D,u),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:D}=this.context.options;for(let o of u){let s=N(D,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.6.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:{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 zl(){let e=new Vl;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 Kl(){let{dryRun:e,verbose:t,...r}=zl(),u=ND("./templates"),i=ND("./configs");GD(u)||(console.error("Template is empty!"),process.exit(1)),GD(i)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:e,verbose:t,options:{...r,templateRootPath:u,configsRootPath:i}}).generate()}Kl().catch(e=>{console.error(e),process.exit(1)});
@@ -1,5 +1,5 @@
1
1
  export * from './common';
2
- export * from './error';
2
+ export * from './common.error';
3
3
  export * from './page.about';
4
4
  export * from './page.executor';
5
5
  export * from './page.home';
@@ -19,6 +19,12 @@
19
19
  "POST /api/login": {
20
20
  "token": "/api/login-token-adfasdfasdf"
21
21
  },
22
+ "POST https://feapi.example.com/api/register": {
23
+ "token": "asdfasdf123123asdfasdf"
24
+ },
25
+ "POST /api/register": {
26
+ "token": "asdfasdf123123asdfasdf"
27
+ },
22
28
  "POST https://api.openai.com/v1/chat/completions": {
23
29
  "id": "chatcmpl-1234567890",
24
30
  "object": "chat.completion",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@brain-toolkit/antd-theme-override": "^0.0.3",
57
- "@qlover/corekit-bridge": "^1.1.2",
58
- "@qlover/fe-corekit": "^1.4.1",
57
+ "@qlover/corekit-bridge": "latest",
58
+ "@qlover/fe-corekit": "latest",
59
59
  "@qlover/logger": "^0.1.1",
60
60
  "@qlover/slice-store-react": "^1.0.8",
61
61
  "@tailwindcss/postcss": "^4.1.8",
@@ -12,6 +12,20 @@ import {
12
12
  import { inject, injectable } from 'inversify';
13
13
  import { UserApiAdapter } from './UserApiAdapter';
14
14
  import { UserApiConfig } from './UserApiBootstarp';
15
+ import {
16
+ LoginResponseData,
17
+ UserAuthApiInterface,
18
+ UserAuthStoreInterface,
19
+ UserAuthState
20
+ } from '@qlover/corekit-bridge';
21
+ import {
22
+ RegisterFormData,
23
+ UserServiceUserInfo
24
+ } from '@/base/services/UserService';
25
+ import { RES_NO_TOKEN } from '@config/Identifier';
26
+ import { AppError } from '@/base/cases/AppError';
27
+
28
+ export type UserApiState = UserAuthState<UserServiceUserInfo>;
15
29
 
16
30
  /**
17
31
  * UserApi
@@ -21,7 +35,12 @@ import { UserApiConfig } from './UserApiBootstarp';
21
35
  *
22
36
  */
23
37
  @injectable()
24
- export class UserApi extends RequestTransaction<UserApiConfig> {
38
+ export class UserApi
39
+ extends RequestTransaction<UserApiConfig>
40
+ implements UserAuthApiInterface<UserApiState>
41
+ {
42
+ protected store: UserAuthStoreInterface<UserApiState> | null = null;
43
+
25
44
  constructor(
26
45
  @inject(FetchAbortPlugin) private abortPlugin: FetchAbortPlugin,
27
46
  @inject(UserApiAdapter) adapter: RequestAdapterFetch
@@ -29,6 +48,18 @@ export class UserApi extends RequestTransaction<UserApiConfig> {
29
48
  super(adapter);
30
49
  }
31
50
 
51
+ getStore(): UserAuthStoreInterface<UserApiState> | null {
52
+ return this.store;
53
+ }
54
+
55
+ /**
56
+ * @override
57
+ * @param store
58
+ */
59
+ setStore(store: UserAuthStoreInterface<UserApiState>): void {
60
+ this.store = store;
61
+ }
62
+
32
63
  stop(request: UserApiConfig): Promise<void> | void {
33
64
  this.abortPlugin.abort(request);
34
65
  }
@@ -52,13 +83,63 @@ export class UserApi extends RequestTransaction<UserApiConfig> {
52
83
  });
53
84
  }
54
85
 
55
- async getUserInfo(): Promise<UserApiGetUserInfoTransaction['response']> {
56
- return this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
57
- }
58
-
86
+ /**
87
+ * @override
88
+ * @param params
89
+ * @returns
90
+ */
59
91
  async login(
60
92
  params: UserApiLoginTransaction['data']
93
+ ): Promise<LoginResponseData> {
94
+ const response = await this.post<UserApiLoginTransaction>(
95
+ '/api/login',
96
+ params
97
+ );
98
+
99
+ if (response.apiCatchResult) {
100
+ throw response.apiCatchResult;
101
+ }
102
+
103
+ if (!response.data.token) {
104
+ throw new AppError(RES_NO_TOKEN);
105
+ }
106
+
107
+ return response.data;
108
+ }
109
+
110
+ /**
111
+ * @override
112
+ * @param params
113
+ * @returns
114
+ */
115
+ register(
116
+ params: RegisterFormData
61
117
  ): Promise<UserApiLoginTransaction['response']> {
62
- return this.post<UserApiLoginTransaction>('/api/login', params);
118
+ return this.post<UserApiLoginTransaction>('/api/register', params);
119
+ }
120
+
121
+ /**
122
+ * @override
123
+ * @returns
124
+ */
125
+ logout(): Promise<void> {
126
+ return Promise.resolve();
127
+ }
128
+
129
+ /**
130
+ * @override
131
+ * @returns
132
+ */
133
+ async getUserInfo(): Promise<
134
+ UserApiGetUserInfoTransaction['response']['data']
135
+ > {
136
+ const response =
137
+ await this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
138
+
139
+ if (response.apiCatchResult) {
140
+ throw response.apiCatchResult;
141
+ }
142
+
143
+ return response.data;
63
144
  }
64
145
  }
@@ -50,3 +50,14 @@ export type UserApiLoginTransaction = UserApiTransaction<
50
50
  >;
51
51
 
52
52
  export type UserApiTestApiCatchResultTransaction = UserApiGetRandomUser;
53
+
54
+ export type UserApiRegisterTransaction = UserApiTransaction<
55
+ {
56
+ username: string;
57
+ email: string;
58
+ password: string;
59
+ confirmPassword: string;
60
+ agreeToTerms: boolean;
61
+ },
62
+ UserApiTransaction['response']['data']
63
+ >;
@@ -55,6 +55,12 @@ export class AppConfig implements EnvConfigInterface {
55
55
  */
56
56
  readonly userTokenStorageKey = '__fe_user_token__';
57
57
 
58
+ /**
59
+ * Storage key for user information
60
+ * @description Injected from VITE_USER_INFO_STORAGE_KEY environment variable
61
+ */
62
+ readonly userInfoStorageKey = '__fe_user_info__';
63
+
58
64
  /**
59
65
  * Available OpenAI model configurations
60
66
  * @description List of supported OpenAI models for the application
@@ -102,4 +108,9 @@ export class AppConfig implements EnvConfigInterface {
102
108
 
103
109
  /** Flag indicating if AI service token is required */
104
110
  readonly aiApiRequireToken = true;
111
+
112
+ /**
113
+ * Project startup href, usually from window.location.href
114
+ */
115
+ readonly bootHref = '';
105
116
  }
@@ -1,19 +1,23 @@
1
- import type { ExecutorPlugin } from '@qlover/fe-corekit';
1
+ import { ExecutorPlugin, type SyncStorageInterface } from '@qlover/fe-corekit';
2
2
  import type {
3
3
  UserApiGetUserInfoTransaction,
4
4
  UserApiLoginTransaction
5
5
  } from '@/base/apis/userApi/UserApiType';
6
6
  import { RouteService } from './RouteService';
7
7
  import {
8
- StoreInterface,
9
- StoreStateInterface,
10
- type StorageTokenInterface
8
+ LoginResponseData,
9
+ type UserAuthApiInterface,
10
+ UserAuthService,
11
+ UserAuthState,
12
+ UserAuthStore
11
13
  } from '@qlover/corekit-bridge';
12
14
  import { inject, injectable } from 'inversify';
13
- import { IOCIdentifier } from '@/core/IOC';
14
15
  import { UserApi } from '@/base/apis/userApi/UserApi';
15
16
  import { AppError } from '@/base/cases/AppError';
16
- import * as errKeys from '@config/Identifier/error';
17
+ import * as errKeys from '@config/Identifier/common.error';
18
+ import { IOCIdentifier } from '@/core/IOC';
19
+ import { AppConfig } from '../cases/AppConfig';
20
+ import { UserApiState } from '../apis/userApi/UserApi';
17
21
 
18
22
  export type UserServiceUserInfo =
19
23
  UserApiGetUserInfoTransaction['response']['data'];
@@ -26,34 +30,43 @@ export interface RegisterFormData {
26
30
  agreeToTerms: boolean;
27
31
  }
28
32
 
29
- class UserServiceState implements StoreStateInterface {
30
- success: boolean = false;
31
- userInfo: UserServiceUserInfo = {
32
- name: '',
33
- email: '',
34
- picture: ''
35
- };
36
- }
37
-
38
33
  @injectable()
39
34
  export class UserService
40
- extends StoreInterface<UserServiceState>
35
+ extends UserAuthService<UserServiceUserInfo>
41
36
  implements ExecutorPlugin
42
37
  {
43
38
  readonly pluginName = 'UserService';
44
39
 
45
40
  constructor(
46
- @inject(UserApi) private userApi: UserApi,
47
- @inject(RouteService) private routerService: RouteService,
48
- @inject(IOCIdentifier.FeApiToken)
49
- private userToken: StorageTokenInterface<string>
41
+ @inject(RouteService) protected routerService: RouteService,
42
+ @inject(UserApi)
43
+ userApi: UserAuthApiInterface<UserAuthState<UserServiceUserInfo>>,
44
+ @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
45
+ @inject(IOCIdentifier.LocalStorageEncrypt)
46
+ storage: SyncStorageInterface<string, string>
50
47
  ) {
51
- super(() => new UserServiceState());
48
+ super(userApi, {
49
+ userStorage: {
50
+ key: appConfig.userInfoStorageKey,
51
+ storage: storage
52
+ },
53
+ credentialStorage: {
54
+ key: appConfig.userTokenStorageKey,
55
+ storage: storage
56
+ }
57
+ });
58
+ }
59
+
60
+ /**
61
+ * @override
62
+ */
63
+ override get store(): UserAuthStore<UserApiState> {
64
+ return super.store as UserAuthStore<UserApiState>;
52
65
  }
53
66
 
54
- selector = {
55
- success: (state: UserServiceState) => state.success
56
- };
67
+ getToken(): string | null {
68
+ return this.store.getCredential();
69
+ }
57
70
 
58
71
  /**
59
72
  * @override
@@ -63,84 +76,49 @@ export class UserService
63
76
  return;
64
77
  }
65
78
 
66
- const userToken = this.userToken.getToken();
79
+ const userToken = this.getToken();
67
80
 
68
81
  if (!userToken) {
69
82
  throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
70
83
  }
71
84
 
72
- const userInfo = await this.userApi.getUserInfo();
73
-
74
- this.emit({
75
- success: true,
76
- userInfo: userInfo.data
77
- });
78
- }
79
-
80
- onSuccess(): void | Promise<void> {
81
- if (this.isAuthenticated()) {
82
- this.emit({ ...this.state, success: true });
83
- } else {
84
- this.logout();
85
+ if (userToken) {
86
+ this.store.authSuccess();
85
87
  }
88
+
89
+ await this.userInfo();
90
+ this.store.authSuccess();
86
91
  }
87
92
 
88
93
  /**
89
94
  * @override
90
95
  */
91
- async login(
92
- params: UserApiLoginTransaction['data']
93
- ): Promise<UserApiGetUserInfoTransaction['response']> {
94
- const response = await this.userApi.login(params);
95
-
96
- if (response.apiCatchResult) {
97
- throw response.apiCatchResult;
98
- }
99
-
100
- if (!response.data.token) {
101
- throw new AppError(errKeys.RES_NO_TOKEN);
102
- }
103
-
104
- this.userToken.setToken(response.data.token);
105
-
106
- const userInfo = await this.userApi.getUserInfo();
107
-
108
- this.emit({
109
- success: true,
110
- userInfo: userInfo.data
111
- });
112
-
113
- return userInfo;
114
- }
96
+ onSuccess(): void {}
115
97
 
116
98
  /**
117
99
  * @override
118
100
  */
119
- logout(): void {
120
- this.reset();
101
+ override async logout(): Promise<void> {
102
+ await super.logout();
103
+
121
104
  this.routerService.reset();
122
105
  this.routerService.gotoLogin();
123
106
  }
124
107
 
125
- /**
126
- * @override
127
- */
128
- reset(): void {
129
- this.userToken.removeToken();
130
- super.reset();
131
- }
132
-
133
- isAuthenticated(): boolean {
134
- return this.state.success;
135
- }
108
+ async register(params: RegisterFormData): Promise<LoginResponseData> {
109
+ const response = (await this.api.register(
110
+ params
111
+ )) as UserApiLoginTransaction['response'];
112
+
113
+ if (response.data?.token) {
114
+ try {
115
+ await this.userInfo({ token: response.data?.token });
116
+ this.store.authSuccess();
117
+ } catch (error) {
118
+ this.store.authFailed(error);
119
+ }
120
+ }
136
121
 
137
- /**
138
- * @override
139
- */
140
- async register(params: RegisterFormData): Promise<unknown> {
141
- return this.login({
142
- username: params.username,
143
- password: params.password
144
- });
122
+ throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
145
123
  }
146
124
  }
@@ -2,14 +2,15 @@
2
2
  import {
3
3
  ApiMockPlugin,
4
4
  EnvConfigInterface,
5
- StorageTokenInterface,
6
5
  RequestCommonPlugin,
7
6
  ApiCatchPlugin,
8
7
  IOCContainerInterface,
9
8
  createIOCFunction,
10
- ServiceIdentifier
9
+ ServiceIdentifier,
10
+ TokenStorage,
11
+ CookieStorage
11
12
  } from '@qlover/corekit-bridge';
12
- import type { JSONSerializer, JSONStorage } from '@qlover/fe-corekit';
13
+ import type { JSONSerializer, ObjectStorage } from '@qlover/fe-corekit';
13
14
  import type { LoggerInterface } from '@qlover/logger';
14
15
  import type { AppConfig } from '@/base/cases/AppConfig';
15
16
  import { Container } from 'inversify';
@@ -69,7 +70,9 @@ export class InversifyContainer implements IOCContainerInterface {
69
70
  */
70
71
  export const IOCIdentifier = Object.freeze({
71
72
  JSON: 'JSON',
72
- JSONStorage: 'JSONStorage',
73
+ LocalStorage: 'LocalStorage',
74
+ LocalStorageEncrypt: 'LocalStorageEncrypt',
75
+ CookieStorage: 'CookieStorage',
73
76
  Logger: 'Logger',
74
77
  FeApiToken: 'FeApiToken',
75
78
  FeApiCommonPlugin: 'FeApiCommonPlugin',
@@ -84,9 +87,11 @@ export const IOCIdentifier = Object.freeze({
84
87
  */
85
88
  export interface IOCIdentifierMap {
86
89
  [IOCIdentifier.JSON]: JSONSerializer;
87
- [IOCIdentifier.JSONStorage]: JSONStorage;
90
+ [IOCIdentifier.LocalStorage]: ObjectStorage<string, string>;
91
+ [IOCIdentifier.LocalStorageEncrypt]: ObjectStorage<string, string>;
92
+ [IOCIdentifier.CookieStorage]: CookieStorage;
88
93
  [IOCIdentifier.Logger]: LoggerInterface;
89
- [IOCIdentifier.FeApiToken]: StorageTokenInterface<string>;
94
+ [IOCIdentifier.FeApiToken]: TokenStorage<string>;
90
95
  [IOCIdentifier.FeApiCommonPlugin]: RequestCommonPlugin;
91
96
  [IOCIdentifier.AppConfig]: EnvConfigInterface;
92
97
  [IOCIdentifier.ApiMockPlugin]: ApiMockPlugin;
@@ -4,7 +4,7 @@ import { IOC } from './IOC';
4
4
  import * as globals from '@/core/globals';
5
5
  import { IocRegister } from './registers';
6
6
  import { BootstrapsRegistry } from './bootstraps';
7
- import { GLOBAL_NO_WINDOW } from '@config/Identifier/error';
7
+ import { GLOBAL_NO_WINDOW } from '@config/Identifier/common.error';
8
8
 
9
9
  export default async function startup({
10
10
  root,
@@ -33,7 +33,7 @@ export default async function startup({
33
33
  },
34
34
  envOptions: {
35
35
  target: appConfig,
36
- source: envSource,
36
+ source: { ...envSource, [envPrefix + 'BOOT_HREF']: root.location.href },
37
37
  prefix: envPrefix,
38
38
  blackList: envBlackList
39
39
  },
@@ -1,6 +1,17 @@
1
1
  // ! global variables, don't import any dependencies and don't have side effects
2
- import { JSONStorage, JSONSerializer, SyncStorage } from '@qlover/fe-corekit';
3
- import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
2
+ import {
3
+ Base64Serializer,
4
+ JSONSerializer,
5
+ ObjectStorage,
6
+ SyncStorage,
7
+ SyncStorageInterface
8
+ } from '@qlover/fe-corekit';
9
+ import {
10
+ ColorFormatter,
11
+ ConsoleHandler,
12
+ CookieStorage,
13
+ Logger
14
+ } from '@qlover/corekit-bridge';
4
15
  import { DialogHandler } from '@/base/cases/DialogHandler';
5
16
  import { loggerStyles } from '@config/common';
6
17
  import { AppConfig } from '@/base/cases/AppConfig';
@@ -26,9 +37,14 @@ export const logger = new Logger({
26
37
  export const JSON = new JSONSerializer();
27
38
 
28
39
  /**
29
- * Override JSONStorage to use the global local storage
40
+ * Override localStorage to use the global local storage
30
41
  */
31
- export const localJsonStorage = new JSONStorage(
32
- localStorage as SyncStorage<string, string>,
33
- JSON
34
- );
42
+ export const localStorage = new SyncStorage(new ObjectStorage(), [
43
+ JSON,
44
+ new Base64Serializer(),
45
+ window.localStorage as unknown as SyncStorageInterface<string>
46
+ ]);
47
+
48
+ export const localStorageEncrypt = localStorage;
49
+
50
+ export const cookieStorage = new CookieStorage();
@@ -1,4 +1,4 @@
1
- import { InversifyContainer, InversifyRegisterInterface } from '../IOC';
1
+ import type { InversifyContainer, InversifyRegisterInterface } from '../IOC';
2
2
 
3
3
  export class RegisterApi implements InversifyRegisterInterface {
4
4
  register(_container: InversifyContainer): void {}
@@ -1,4 +1,4 @@
1
- import { FetchAbortPlugin, JSONStorage } from '@qlover/fe-corekit';
1
+ import { FetchAbortPlugin } from '@qlover/fe-corekit';
2
2
  import { Logger } from '@qlover/logger';
3
3
  import {
4
4
  InversifyContainer,
@@ -7,20 +7,21 @@ import {
7
7
  IocRegisterOptions
8
8
  } from '@/core/IOC';
9
9
  import {
10
- UserToken,
11
10
  RequestCommonPlugin,
12
11
  ApiMockPlugin,
13
12
  ApiCatchPlugin,
14
13
  ThemeService,
15
- IOCManagerInterface
14
+ IOCManagerInterface,
15
+ TokenStorage
16
16
  } from '@qlover/corekit-bridge';
17
17
  import mockDataJson from '@config/feapi.mock.json';
18
18
  import { RequestStatusCatcher } from '@/base/cases/RequestStatusCatcher';
19
19
  import { themeConfig } from '@config/theme';
20
- import { localJsonStorage, logger } from '../globals';
20
+ import { localStorage, logger } from '../globals';
21
21
  import { I18nService } from '@/base/services/I18nService';
22
22
  import { RouteService } from '@/base/services/RouteService';
23
23
  import { baseRoutes } from '@config/app.router';
24
+ import { UserService } from '@/base/services/UserService';
24
25
 
25
26
  export class RegisterCommon implements InversifyRegisterInterface {
26
27
  register(
@@ -30,21 +31,19 @@ export class RegisterCommon implements InversifyRegisterInterface {
30
31
  ): void {
31
32
  const AppConfig = container.get(IOCIdentifier.AppConfig);
32
33
 
33
- const userToken = new UserToken({
34
- storageKey: AppConfig.userTokenStorageKey,
35
- storage: container.get(JSONStorage)
34
+ const feApiToken = new TokenStorage(AppConfig.userTokenStorageKey, {
35
+ storage: container.get(IOCIdentifier.LocalStorageEncrypt)
36
36
  });
37
37
  const feApiAbort = new FetchAbortPlugin();
38
38
  const feApiRequestCommonPlugin = new RequestCommonPlugin({
39
39
  tokenPrefix: AppConfig.openAiTokenPrefix,
40
40
  requiredToken: true,
41
- token: () => userToken.getToken()
41
+ token: () => container.get(UserService).getToken()
42
42
  });
43
43
 
44
44
  container.bind(FetchAbortPlugin, feApiAbort);
45
- container.bind(UserToken, userToken);
46
45
 
47
- container.bind(IOCIdentifier.FeApiToken, userToken);
46
+ container.bind(IOCIdentifier.FeApiToken, feApiToken);
48
47
  container.bind(IOCIdentifier.FeApiCommonPlugin, feApiRequestCommonPlugin);
49
48
  container.bind(
50
49
  IOCIdentifier.ApiMockPlugin,
@@ -62,7 +61,7 @@ export class RegisterCommon implements InversifyRegisterInterface {
62
61
  ThemeService,
63
62
  new ThemeService({
64
63
  ...themeConfig,
65
- storage: localJsonStorage
64
+ storage: localStorage
66
65
  })
67
66
  );
68
67
 
@@ -1,4 +1,4 @@
1
- import { localJsonStorage } from '../globals';
1
+ import { localStorage } from '../globals';
2
2
  import { JSONStorageController } from '@/uikit/controllers/JSONStorageController';
3
3
  import { ProcesserExecutor } from '@/base/services/ProcesserExecutor';
4
4
  import { UserService } from '@/base/services/UserService';
@@ -8,7 +8,7 @@ import { I18nKeyErrorPlugin } from '@/base/cases/I18nKeyErrorPlugin';
8
8
 
9
9
  export class RegisterControllers implements InversifyRegisterInterface {
10
10
  register(container: InversifyContainer): void {
11
- const jsonStorageController = new JSONStorageController(localJsonStorage);
11
+ const jsonStorageController = new JSONStorageController(localStorage);
12
12
 
13
13
  container.bind(JSONStorageController, jsonStorageController);
14
14
 
@@ -1,6 +1,13 @@
1
- import { JSONSerializer, JSONStorage } from '@qlover/fe-corekit';
1
+ import { JSONSerializer } from '@qlover/fe-corekit';
2
2
  import type { IOCManagerInterface } from '@qlover/corekit-bridge';
3
- import { dialogHandler, JSON, localJsonStorage, logger } from '../globals';
3
+ import {
4
+ cookieStorage,
5
+ dialogHandler,
6
+ JSON,
7
+ localStorage,
8
+ localStorageEncrypt,
9
+ logger
10
+ } from '../globals';
4
11
  import {
5
12
  type InversifyContainer,
6
13
  type InversifyRegisterInterface,
@@ -25,7 +32,8 @@ export class RegisterGlobals implements InversifyRegisterInterface {
25
32
  container.bind(Logger, logger);
26
33
  container.bind(IOCIdentifier.Logger, logger);
27
34
 
28
- container.bind(JSONStorage, localJsonStorage);
29
- container.bind(IOCIdentifier.JSONStorage, localJsonStorage);
35
+ container.bind(IOCIdentifier.LocalStorage, localStorage);
36
+ container.bind(IOCIdentifier.LocalStorageEncrypt, localStorageEncrypt);
37
+ container.bind(IOCIdentifier.CookieStorage, cookieStorage);
30
38
  }
31
39
  }
@@ -1,6 +1,6 @@
1
1
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
2
2
  import { PAGE_404_TITLE } from '@config/Identifier/common';
3
- import { NOT_FOUND_COMPONENT } from '@config/Identifier/error';
3
+ import { NOT_FOUND_COMPONENT } from '@config/Identifier/common.error';
4
4
 
5
5
  export default function NotFound({ route }: { route?: string }) {
6
6
  const { t } = useBaseRoutePage();
@@ -6,7 +6,7 @@ import BaseHeader from '../../uikit/components/BaseHeader';
6
6
 
7
7
  export default function Layout() {
8
8
  const userService = IOC(UserService);
9
- useStore(userService, (state) => state.success);
9
+ useStore(userService.store);
10
10
 
11
11
  // If user is authenticated, redirect to home page
12
12
  if (userService.isAuthenticated()) {
@@ -5,7 +5,6 @@ import { IOC } from '@/core/IOC';
5
5
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
6
6
  import { RouteService } from '@/base/services/RouteService';
7
7
  import { UserService } from '@/base/services/UserService';
8
- import { useStore } from '@/uikit/hooks/useStore';
9
8
  import * as i18nKeys from '@config/Identifier/page.login';
10
9
  import LocaleLink from '@/uikit/components/LocaleLink';
11
10
 
@@ -18,7 +17,6 @@ export default function LoginPage() {
18
17
  const { t } = useBaseRoutePage();
19
18
  const userService = IOC(UserService);
20
19
  const AppConfig = IOC('AppConfig');
21
- useStore(userService);
22
20
  const [loading, setLoading] = useState(false);
23
21
 
24
22
  const handleLogin = async (values: LoginFormData) => {
@@ -5,14 +5,12 @@ import { IOC } from '@/core/IOC';
5
5
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
6
6
  import { RouteService } from '@/base/services/RouteService';
7
7
  import { RegisterFormData, UserService } from '@/base/services/UserService';
8
- import { useStore } from '@/uikit/hooks/useStore';
9
8
  import * as i18nKeys from '@config/Identifier/page.register';
10
9
 
11
10
  export default function RegisterPage() {
12
11
  const { t } = useBaseRoutePage();
13
12
  const userService = IOC(UserService);
14
13
  const AppConfig = IOC('AppConfig');
15
- useStore(userService);
16
14
  const [loading, setLoading] = useState(false);
17
15
  const [form] = Form.useForm();
18
16
 
@@ -1,6 +1,6 @@
1
1
  import { Button } from 'antd';
2
2
  import { useBaseRoutePage } from '@/uikit/contexts/BaseRouteContext';
3
- import * as ErrorIdentifierList from '@config/Identifier/error';
3
+ import * as ErrorIdentifierList from '@config/Identifier/common.error';
4
4
  import * as i18nKeys from '@config/Identifier/page.identifiter';
5
5
 
6
6
  export default function ErrorIdentifierPage() {
@@ -46,7 +46,7 @@ export default function JSONStoragePage() {
46
46
  {t(i18nKeys.PAGE_JSONSTORAGE_CURRENT_VALUE)}:{' '}
47
47
  </span>
48
48
  <span className="font-semibold text-text">
49
- {controllerState.testKey1}
49
+ {JSON.stringify(controllerState.testKey1)}
50
50
  </span>
51
51
  </div>
52
52
  </div>
@@ -5,7 +5,7 @@ import { RouteMeta } from '@/base/types/Page';
5
5
  import { createContext } from 'react';
6
6
  import merge from 'lodash/merge';
7
7
  import i18nConfig from '@config/i18n';
8
- import { WITHIN_PAGE_PROVIDER } from '@config/Identifier/error';
8
+ import { WITHIN_PAGE_PROVIDER } from '@config/Identifier/common.error';
9
9
 
10
10
  const { defaultNS } = i18nConfig;
11
11
 
@@ -1,9 +1,9 @@
1
- import { JSONStorage } from '@qlover/fe-corekit';
2
1
  import {
3
2
  StoreInterface,
4
3
  type StoreStateInterface
5
4
  } from '@qlover/corekit-bridge';
6
- import { random } from 'lodash';
5
+ import { SyncStorageInterface } from '@qlover/fe-corekit';
6
+ import random from 'lodash/random';
7
7
 
8
8
  interface JSONStoragePageState extends StoreStateInterface {
9
9
  testKey1?: number;
@@ -17,7 +17,7 @@ export class JSONStorageController extends StoreInterface<JSONStoragePageState>
17
17
  requestTimeout: (state: JSONStoragePageState) => state.requestTimeout
18
18
  };
19
19
 
20
- constructor(private storage: JSONStorage) {
20
+ constructor(private storage: SyncStorageInterface<string, number>) {
21
21
  super(() => ({
22
22
  testKey1: storage.getItem('testKey1') ?? 0,
23
23
  testKey2: storage.getItem('testKey2') ?? 0,
@@ -6,7 +6,7 @@ import { useStore } from '../hooks/useStore';
6
6
  export function UserAuthProvider({ children }: { children: React.ReactNode }) {
7
7
  const userService = IOC(UserService);
8
8
 
9
- useStore(userService);
9
+ useStore(userService.store);
10
10
 
11
11
  if (!userService.isAuthenticated()) {
12
12
  return <Loading fullscreen />;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlover/create-app",
3
- "version": "0.4.6",
3
+ "version": "0.6.0",
4
4
  "description": "Create a new app with a single command",
5
5
  "private": false,
6
6
  "type": "module",