@qlover/create-app 0.4.6 → 0.5.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 +15 -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 +84 -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 +68 -82
  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,20 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ #### ✨ Features
8
+
9
+ - **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))
10
+ - Updated package.json to reference local corekit dependencies for improved development.
11
+ - Added new error identifiers in common.error.ts for better error management.
12
+ - Refactored UserApi to implement registration and improved login handling with token validation.
13
+ - Introduced common error handling in UserService and updated related components to utilize new error identifiers.
14
+ - Enhanced storage management by integrating new storage interfaces and updating related services.
15
+
16
+ These changes aim to streamline user authentication processes and improve error reporting across the application.
17
+
3
18
  ## 0.4.6
4
19
 
5
20
  ### 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.5.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.5.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": "file:../../../corekit-bridge",
58
+ "@qlover/fe-corekit": "file:../../../fe-corekit",
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,17 @@ 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
+ } from '@qlover/corekit-bridge';
20
+ import {
21
+ RegisterFormData,
22
+ UserServiceUserInfo
23
+ } from '@/base/services/UserService';
24
+ import { RES_NO_TOKEN } from '@config/Identifier';
25
+ import { AppError } from '@/base/cases/AppError';
15
26
 
16
27
  /**
17
28
  * UserApi
@@ -21,7 +32,12 @@ import { UserApiConfig } from './UserApiBootstarp';
21
32
  *
22
33
  */
23
34
  @injectable()
24
- export class UserApi extends RequestTransaction<UserApiConfig> {
35
+ export class UserApi
36
+ extends RequestTransaction<UserApiConfig>
37
+ implements UserAuthApiInterface<UserServiceUserInfo>
38
+ {
39
+ protected store: UserAuthStoreInterface<UserServiceUserInfo> | null = null;
40
+
25
41
  constructor(
26
42
  @inject(FetchAbortPlugin) private abortPlugin: FetchAbortPlugin,
27
43
  @inject(UserApiAdapter) adapter: RequestAdapterFetch
@@ -29,6 +45,18 @@ export class UserApi extends RequestTransaction<UserApiConfig> {
29
45
  super(adapter);
30
46
  }
31
47
 
48
+ getStore(): UserAuthStoreInterface<UserServiceUserInfo> | null {
49
+ return this.store;
50
+ }
51
+
52
+ /**
53
+ * @override
54
+ * @param store
55
+ */
56
+ setStore(store: UserAuthStoreInterface<UserServiceUserInfo>): void {
57
+ this.store = store;
58
+ }
59
+
32
60
  stop(request: UserApiConfig): Promise<void> | void {
33
61
  this.abortPlugin.abort(request);
34
62
  }
@@ -52,13 +80,63 @@ export class UserApi extends RequestTransaction<UserApiConfig> {
52
80
  });
53
81
  }
54
82
 
55
- async getUserInfo(): Promise<UserApiGetUserInfoTransaction['response']> {
56
- return this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
57
- }
58
-
83
+ /**
84
+ * @override
85
+ * @param params
86
+ * @returns
87
+ */
59
88
  async login(
60
89
  params: UserApiLoginTransaction['data']
90
+ ): Promise<LoginResponseData> {
91
+ const response = await this.post<UserApiLoginTransaction>(
92
+ '/api/login',
93
+ params
94
+ );
95
+
96
+ if (response.apiCatchResult) {
97
+ throw response.apiCatchResult;
98
+ }
99
+
100
+ if (!response.data.token) {
101
+ throw new AppError(RES_NO_TOKEN);
102
+ }
103
+
104
+ return response.data;
105
+ }
106
+
107
+ /**
108
+ * @override
109
+ * @param params
110
+ * @returns
111
+ */
112
+ register(
113
+ params: RegisterFormData
61
114
  ): Promise<UserApiLoginTransaction['response']> {
62
- return this.post<UserApiLoginTransaction>('/api/login', params);
115
+ return this.post<UserApiLoginTransaction>('/api/register', params);
116
+ }
117
+
118
+ /**
119
+ * @override
120
+ * @returns
121
+ */
122
+ logout(): Promise<void> {
123
+ return Promise.resolve();
124
+ }
125
+
126
+ /**
127
+ * @override
128
+ * @returns
129
+ */
130
+ async getUserInfo(): Promise<
131
+ UserApiGetUserInfoTransaction['response']['data']
132
+ > {
133
+ const response =
134
+ await this.get<UserApiGetUserInfoTransaction>('/api/userinfo');
135
+
136
+ if (response.apiCatchResult) {
137
+ throw response.apiCatchResult;
138
+ }
139
+
140
+ return response.data;
63
141
  }
64
142
  }
@@ -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,21 @@
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
+ UserAuthStore
11
12
  } from '@qlover/corekit-bridge';
12
13
  import { inject, injectable } from 'inversify';
13
- import { IOCIdentifier } from '@/core/IOC';
14
14
  import { UserApi } from '@/base/apis/userApi/UserApi';
15
15
  import { AppError } from '@/base/cases/AppError';
16
- import * as errKeys from '@config/Identifier/error';
16
+ import * as errKeys from '@config/Identifier/common.error';
17
+ import { IOCIdentifier } from '@/core/IOC';
18
+ import { AppConfig } from '../cases/AppConfig';
17
19
 
18
20
  export type UserServiceUserInfo =
19
21
  UserApiGetUserInfoTransaction['response']['data'];
@@ -26,34 +28,47 @@ export interface RegisterFormData {
26
28
  agreeToTerms: boolean;
27
29
  }
28
30
 
29
- class UserServiceState implements StoreStateInterface {
30
- success: boolean = false;
31
- userInfo: UserServiceUserInfo = {
32
- name: '',
33
- email: '',
34
- picture: ''
35
- };
36
- }
37
-
38
31
  @injectable()
39
32
  export class UserService
40
- extends StoreInterface<UserServiceState>
33
+ extends UserAuthService<UserServiceUserInfo>
41
34
  implements ExecutorPlugin
42
35
  {
43
36
  readonly pluginName = 'UserService';
44
37
 
45
38
  constructor(
46
- @inject(UserApi) private userApi: UserApi,
47
- @inject(RouteService) private routerService: RouteService,
48
- @inject(IOCIdentifier.FeApiToken)
49
- private userToken: StorageTokenInterface<string>
39
+ @inject(RouteService) protected routerService: RouteService,
40
+ @inject(UserApi) userApi: UserAuthApiInterface<UserServiceUserInfo>,
41
+ @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
42
+ @inject(IOCIdentifier.LocalStorageEncrypt)
43
+ storage: SyncStorageInterface<string, string>
50
44
  ) {
51
- super(() => new UserServiceState());
45
+ super({
46
+ api: userApi,
47
+ userStorage: {
48
+ key: appConfig.userInfoStorageKey,
49
+ storage: storage
50
+ },
51
+ credentialStorage: {
52
+ key: appConfig.userTokenStorageKey,
53
+ storage: storage
54
+ }
55
+ });
56
+
57
+ // FIXME:
58
+ // load credential from storage
59
+ // this.store.setCredential(this.store.getCredentialStorage()?.get() ?? '');
60
+ }
61
+
62
+ /**
63
+ * @override
64
+ */
65
+ override get store(): UserAuthStore<UserServiceUserInfo> {
66
+ return super.store as UserAuthStore<UserServiceUserInfo>;
52
67
  }
53
68
 
54
- selector = {
55
- success: (state: UserServiceState) => state.success
56
- };
69
+ getToken(): string | null {
70
+ return this.store.getCredential();
71
+ }
57
72
 
58
73
  /**
59
74
  * @override
@@ -63,84 +78,55 @@ export class UserService
63
78
  return;
64
79
  }
65
80
 
66
- const userToken = this.userToken.getToken();
81
+ const userToken = this.getToken();
67
82
 
68
83
  if (!userToken) {
69
84
  throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
70
85
  }
71
86
 
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();
87
+ if (userToken) {
88
+ this.store.authSuccess();
85
89
  }
90
+
91
+ await this.userInfo();
92
+ this.store.authSuccess();
86
93
  }
87
94
 
88
95
  /**
89
96
  * @override
90
97
  */
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;
98
+ onSuccess(): void {
99
+ // if (this.isAuthenticated()) {
100
+ // this.store.authSuccess();
101
+ // } else {
102
+ // this.logout();
103
+ // }
114
104
  }
115
105
 
116
106
  /**
117
107
  * @override
118
108
  */
119
- logout(): void {
120
- this.reset();
109
+ override async logout(): Promise<void> {
110
+ await super.logout();
111
+
121
112
  this.routerService.reset();
122
113
  this.routerService.gotoLogin();
123
114
  }
124
115
 
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
- }
116
+ async register(params: RegisterFormData): Promise<LoginResponseData> {
117
+ const response = (await this.api.register(
118
+ params
119
+ )) as UserApiLoginTransaction['response'];
120
+
121
+ if (response.data?.token) {
122
+ try {
123
+ await this.userInfo({ token: response.data?.token });
124
+ this.store.authSuccess();
125
+ } catch (error) {
126
+ this.store.authFailed(error);
127
+ }
128
+ }
136
129
 
137
- /**
138
- * @override
139
- */
140
- async register(params: RegisterFormData): Promise<unknown> {
141
- return this.login({
142
- username: params.username,
143
- password: params.password
144
- });
130
+ throw new AppError(errKeys.LOCAL_NO_USER_TOKEN);
145
131
  }
146
132
  }
@@ -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.5.0",
4
4
  "description": "Create a new app with a single command",
5
5
  "private": false,
6
6
  "type": "module",