@qlover/create-app 1.0.0 → 1.0.2

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 (85) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/Identifier/api.ts +7 -0
  5. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +8 -0
  6. package/dist/templates/next-app/config/common.ts +1 -1
  7. package/dist/templates/next-app/config/i18n/HomeI18n.ts +2 -0
  8. package/dist/templates/next-app/config/i18n/register18n.ts +2 -1
  9. package/dist/templates/next-app/config/route.ts +9 -0
  10. package/dist/templates/next-app/migrations/schema/UserSchema.ts +1 -1
  11. package/dist/templates/next-app/next.config.ts +5 -4
  12. package/dist/templates/next-app/package.json +7 -8
  13. package/dist/templates/next-app/public/locales/en.json +4 -1
  14. package/dist/templates/next-app/public/locales/zh.json +4 -1
  15. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +18 -0
  16. package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/LoginForm.tsx +2 -1
  17. package/dist/templates/next-app/src/app/[locale]/{login → auth/login}/page.tsx +4 -5
  18. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +8 -0
  19. package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/RegisterForm.tsx +24 -3
  20. package/dist/templates/next-app/src/app/[locale]/{register → auth/register}/page.tsx +4 -5
  21. package/dist/templates/next-app/src/app/[locale]/page.tsx +18 -45
  22. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +13 -13
  23. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +11 -0
  24. package/dist/templates/next-app/src/app/api/callback/route.ts +49 -0
  25. package/dist/templates/next-app/src/base/cases/AppConfig.ts +2 -0
  26. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +3 -6
  27. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -1
  28. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +13 -15
  29. package/dist/templates/next-app/src/base/cases/RouterService.ts +2 -7
  30. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -6
  31. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +53 -0
  32. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -10
  33. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -3
  34. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +1 -1
  35. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -18
  36. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +10 -5
  37. package/dist/templates/next-app/src/base/services/{appApi/AppApiRequester.ts → AppApiRequester.ts} +16 -11
  38. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +110 -0
  39. package/dist/templates/next-app/src/base/services/I18nService.ts +1 -7
  40. package/dist/templates/next-app/src/base/services/ResourceService.ts +1 -4
  41. package/dist/templates/next-app/src/base/services/UserService.ts +28 -17
  42. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +5 -7
  43. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +4 -3
  44. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +24 -16
  45. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -5
  46. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +15 -18
  47. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -5
  48. package/dist/templates/next-app/src/core/globals.ts +1 -0
  49. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +2 -8
  50. package/dist/templates/next-app/src/i18n/routing.ts +8 -3
  51. package/dist/templates/next-app/src/lib/supabase/client.ts +8 -0
  52. package/dist/templates/next-app/src/lib/supabase/conts.ts +2 -0
  53. package/dist/templates/next-app/src/lib/supabase/proxy.ts +84 -0
  54. package/dist/templates/next-app/src/lib/supabase/server.ts +38 -0
  55. package/dist/templates/next-app/src/proxy.ts +8 -1
  56. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +5 -2
  57. package/dist/templates/next-app/src/server/NextApiServer.ts +2 -9
  58. package/dist/templates/next-app/src/server/PagesRouteParams.ts +3 -4
  59. package/dist/templates/next-app/src/server/ServerAuth.ts +18 -12
  60. package/dist/templates/next-app/src/server/SupabaseBridge.ts +66 -59
  61. package/dist/templates/next-app/src/server/controllers/UserController.ts +7 -2
  62. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +4 -0
  63. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -1
  64. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +7 -1
  65. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -3
  66. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -3
  67. package/dist/templates/next-app/src/server/services/UserService.ts +71 -51
  68. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -3
  69. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -6
  70. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +68 -0
  71. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -3
  72. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -6
  73. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +29 -0
  74. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +21 -28
  75. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +20 -0
  76. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +2 -2
  77. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +3 -17
  78. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +8 -0
  79. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +42 -0
  80. package/package.json +1 -1
  81. package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +0 -25
  82. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +0 -78
  83. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +0 -25
  84. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +0 -78
  85. package/dist/templates/next-app/src/server/port/UserControllerInerface.ts +0 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ #### ✨ Features
8
+
9
+ - **next-app:** Enhance authentication flow and update configurations ([23fd553](https://github.com/qlover/fe-base/commit/23fd55372b1196d183f423a52607d6eb1c09ca82)) ([#579](https://github.com/qlover/fe-base/pull/579))
10
+ - Added `transpilePackages` for necessary routing in `next.config.ts`, improving package handling.
11
+ - Updated package versions for `@brain-toolkit/antd-blocks` and `@brain-toolkit/react-kit` in `package.json` for better stability.
12
+ - Refactored user schema to change `id` type from `number` to `string` for consistency.
13
+ - Introduced new routes for login and registration in `route.ts`, enhancing navigation.
14
+ - Added email verification prompts in registration and login processes, improving user experience.
15
+ - Implemented new API callback routes for handling authentication responses, streamlining user management.
16
+ - Enhanced translation utilities for better internationalization support across the application.
17
+
18
+ ## 1.0.1
19
+
20
+ ### Patch Changes
21
+
3
22
  ## 1.0.0
4
23
 
5
24
  ### Major 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&&z.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&&z.default.stdin.isTTY&&this.#r&&(Je.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:Y.success,text:t})}fail(t){return this.stopAndPersist({symbol:Y.error,text:t})}warn(t){return this.stopAndPersist({symbol:Y.warning,text:t})}info(t){return this.stopAndPersist({symbol:Y.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function 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 bt=require("fs");var S=require("path"),A=require("fs"),cr=h(lr(),1);var _e=require("fs"),J=class{static ensureDir(t){(0,_e.existsSync)(t)||(0,_e.mkdirSync)(t,{recursive:!0})}};var{copyFile:rs,stat:us}=A.promises,me=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,S.join)(t,this.ignoreFile);if(!(0,A.existsSync)(r))return;let D=(0,A.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 A.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,S.join)(t,o),a=(0,S.join)(r,o);if(u&&u.ignores(o))return;if(J.ensureDir((0,S.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}){J.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var k=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,k.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,k.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,k.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,bt.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new me((0,x.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,x.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,x.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,x.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,x.join)(D,o),a=(0,x.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var At={name:"@qlover/create-app",version:"1.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","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 HD.Command;return e.version(At.version,"-v, --version","Show version").description(At.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 YD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Ul(),i=(0,vt.resolve)(e,"./templates"),D=(0,vt.resolve)(e,"./configs");(0,xt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}YD(__dirname).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 A.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,S.join)(t,o),a=(0,S.join)(r,o);if(u&&u.ignores(o))return;if(J.ensureDir((0,S.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}){J.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var k=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,k.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,k.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,k.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,bt.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new me((0,x.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,x.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,x.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,x.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,x.join)(D,o),a=(0,x.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var At={name:"@qlover/create-app",version:"1.0.2",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","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 HD.Command;return e.version(At.version,"-v, --version","Show version").description(At.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 YD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Ul(),i=(0,vt.resolve)(e,"./templates"),D=(0,vt.resolve)(e,"./configs");(0,xt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}YD(__dirname).catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,pD=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),hD=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(n,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#n.frames.length,this.#c=t);let{frames:r}=this.#n,u=r[this.#i];this.color&&(u=E[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",n=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+n+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#D;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#D=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#D=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&je.hide(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&(this.#r=!0,We.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&je.show(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&this.#r&&(We.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:k.success,text:t})}fail(t){return this.stopAndPersist({symbol:k.error,text:t})}warn(t){return this.stopAndPersist({symbol:k.warning,text:t})}info(t){return this.stopAndPersist({symbol:k.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",n=t.text??this.text,s=typeof n=="string"?(i?" ":"")+n:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function zt(e){return new He(e)}async function Kt(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:n}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=zt(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(n===void 0?void 0:typeof n=="string"?n:n(s)),s}}import{existsSync as Nn}from"fs";var Dr=ue(ir(),1);import{dirname as QD,join as Ze}from"path";import{existsSync as es,readFileSync as ts,promises as nr}from"fs";import{existsSync as JD,mkdirSync as ZD}from"fs";var H=class{static ensureDir(t){JD(t)||ZD(t,{recursive:!0})}};var{copyFile:rs,stat:us}=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(!es(r))return;let n=ts(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(n)}async copyFiles(t,r,u,i){let n=await nr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"1.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(n)}async copyFiles(t,r,u,i){let n=await nr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"1.0.2",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
@@ -5,6 +5,13 @@
5
5
  */
6
6
  export const API_USER_NOT_FOUND = 'api:user__not_found';
7
7
 
8
+ /**
9
+ * @description 用户未验证
10
+ * @localZh 用户未验证
11
+ * @localEn User not verified
12
+ */
13
+ export const API_USER_NOT_VERIFIED = 'api:user__not_verfified';
14
+
8
15
  /**
9
16
  * @description 用户已存在
10
17
  * @localZh 用户已存在
@@ -167,3 +167,11 @@ export const PAGE_REGISTER_LOGIN_LINK = 'page_register:login_link';
167
167
  * @localEn Please agree to the Terms of Service and Privacy Policy
168
168
  */
169
169
  export const PAGE_REGISTER_TERMS_REQUIRED = 'page_register:terms_required';
170
+
171
+ /**
172
+ * @description 注册后需要验证邮箱
173
+ * @localZh 请查收邮箱完成账号注册
174
+ * @localEn Please check your email to complete the registration
175
+ */
176
+ export const PAGE_REGISTER_EMAIL_VERIFY_TITLE =
177
+ 'page_register:email_verify_title';
@@ -26,7 +26,7 @@ export const useLocaleRoutes = true;
26
26
  * - true: 使用API获取本地化数据,可以在 /admin/locales 页面中对他进行修改
27
27
  * - false: 不使用API获取本地化数据,直接使用 `@brain-toolkit/ts2locales` 生成的json数据
28
28
  */
29
- export const useApiLocales = true;
29
+ export const useApiLocales = false;
30
30
 
31
31
  /**
32
32
  * 是否在在 useWarnTranslations 中警告缺失的翻译,而不是抛出错误
@@ -8,6 +8,8 @@ import * as i18nKeys from '../Identifier/pages/page.home';
8
8
  */
9
9
  export type HomeI18nInterface = typeof homeI18n;
10
10
 
11
+ export const homeI18nNamespace = 'page_home';
12
+
11
13
  export const homeI18n = Object.freeze({
12
14
  // basic meta properties
13
15
  title: i18nKeys.PAGE_HOME_TITLE,
@@ -39,5 +39,6 @@ export const register18n = Object.freeze({
39
39
  have_account: i18nKeys.PAGE_REGISTER_HAVE_ACCOUNT,
40
40
  confirm_password: i18nKeys.PAGE_REGISTER_CONFIRM_PASSWORD,
41
41
  terms_required: i18nKeys.PAGE_REGISTER_TERMS_REQUIRED,
42
- login_link: i18nKeys.PAGE_REGISTER_LOGIN_LINK
42
+ login_link: i18nKeys.PAGE_REGISTER_LOGIN_LINK,
43
+ email_verify: i18nKeys.PAGE_REGISTER_EMAIL_VERIFY_TITLE
43
44
  });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 登录页面路由地址
3
+ */
4
+ export const ROUTE_LOGIN = '/auth/login' as const;
5
+
6
+ /**
7
+ * 注册页面路由地址
8
+ */
9
+ export const ROUTE_REGISTER = '/auth/register' as const;
@@ -9,7 +9,7 @@ export const UserRole = {
9
9
  export type UserRoleType = (typeof UserRole)[keyof typeof UserRole];
10
10
 
11
11
  export const userSchema = z.object({
12
- id: z.number(),
12
+ id: z.string(),
13
13
  role: z.nativeEnum(UserRole),
14
14
  email: z.string().email(),
15
15
  password: z.string(),
@@ -12,10 +12,11 @@ generateLocales().catch((error) => {
12
12
  const nextConfig: NextConfig = {
13
13
  // reactStrictMode: false,
14
14
  // turbopack 在接下本地 file: 依赖时支持还不够好
15
- // turbopack: {
16
- // root: __dirname // 明确指定根目录
17
- // },
18
- transpilePackages: ['@qlover/corekit-bridge', '@qlover/fe-corekit'],
15
+ turbopack: {
16
+ root: __dirname // 明确指定根目录
17
+ },
18
+ // pages 路由需要
19
+ transpilePackages: ['@qlover/fe-corekit', '@qlover/corekit-bridge'],
19
20
  env: {
20
21
  APP_ENV: process.env.APP_ENV
21
22
  }
@@ -22,13 +22,12 @@
22
22
  "@ant-design/icons": "^6.0.0",
23
23
  "@ant-design/nextjs-registry": "^1.3.0",
24
24
  "@ant-design/v5-patch-for-react-19": "^1.0.3",
25
- "@brain-toolkit/antd-blocks": "^0.0.1",
25
+ "@brain-toolkit/antd-blocks": "^0.1.2",
26
26
  "@brain-toolkit/antd-theme-override": "^0.0.3",
27
- "@brain-toolkit/bridge": "^0.0.1",
28
- "@brain-toolkit/react-kit": "^0.1.0",
29
- "@qlover/corekit-bridge": "../../../corekit-bridge",
30
- "@qlover/fe-corekit": "../../../fe-corekit",
31
- "@qlover/logger": "../../../logger",
27
+ "@brain-toolkit/react-kit": "^0.2.2",
28
+ "@qlover/corekit-bridge": "^2.1.1",
29
+ "@qlover/fe-corekit": "^3.1.1",
30
+ "@qlover/logger": "^1.0.0",
32
31
  "@qlover/slice-store-react": "^1.4.1",
33
32
  "@supabase/postgrest-js": "^2.87.1",
34
33
  "@supabase/ssr": "^0.7.0",
@@ -41,7 +40,7 @@
41
40
  "inversify": "^7.8.1",
42
41
  "jsonwebtoken": "^9.0.2",
43
42
  "lodash": "^4.17.21",
44
- "next": "^16.0.3",
43
+ "next": "^16.1.6",
45
44
  "next-intl": "^4.5.8",
46
45
  "next-themes": "^0.4.6",
47
46
  "openai": "^5.23.0",
@@ -55,7 +54,7 @@
55
54
  "@brain-toolkit/ts2locales": "^0.2.3",
56
55
  "@eslint/eslintrc": "^3",
57
56
  "@qlover/env-loader": "^0.3.0",
58
- "@qlover/eslint-plugin": "latest",
57
+ "@qlover/eslint-plugin": "^2.0.0",
59
58
  "@tailwindcss/postcss": "^4",
60
59
  "@types/lodash": "^4.17.20",
61
60
  "@types/ms": "^2.1.0",
@@ -175,5 +175,8 @@
175
175
  "page_register:privacy_link": "Privacy Policy",
176
176
  "page_register:have_account": "Already have an account?",
177
177
  "page_register:login_link": "Sign in",
178
- "page_register:terms_required": "Please agree to the Terms of Service and Privacy Policy"
178
+ "page_register:terms_required": "Please agree to the Terms of Service and Privacy Policy",
179
+ "api:user__not_verfified": "User not verified",
180
+ "page_register:email_verify_title": "Please check your email to complete the registration",
181
+ "page_home:welcome_with_user": "Welcome {email} to the homepage"
179
182
  }
@@ -175,5 +175,8 @@
175
175
  "page_register:privacy_link": "隐私政策",
176
176
  "page_register:have_account": "已有账号?",
177
177
  "page_register:login_link": "登录",
178
- "page_register:terms_required": "请同意服务条款和隐私政策"
178
+ "page_register:terms_required": "请同意服务条款和隐私政策",
179
+ "api:user__not_verfified": "用户未验证",
180
+ "page_register:email_verify_title": "请查收邮箱完成账号注册",
181
+ "page_home:welcome_with_user": "欢迎 {email} 来到主页"
179
182
  }
@@ -0,0 +1,18 @@
1
+ import type { PageLayoutProps } from '@/base/types/AppPageRouter';
2
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
3
+ import { redirect } from '@/i18n/routing';
4
+ import { AppPageRouteParams } from '@/server/AppPageRouteParams';
5
+ import { ServerAuth } from '@/server/ServerAuth';
6
+
7
+ export default async function AuthRootPage(props: PageLayoutProps) {
8
+ const pageParams = new AppPageRouteParams(await props.params!);
9
+ const locale = pageParams.getLocale();
10
+
11
+ if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
12
+ console.info('> User already logged in, redirecting to home page');
13
+
14
+ return redirect({ href: '/', locale: locale });
15
+ }
16
+
17
+ return <>{props.children}</>;
18
+ }
@@ -9,6 +9,7 @@ import { useIOC } from '@/uikit/hook/useIOC';
9
9
  import { useWarnTranslations } from '@/uikit/hook/useWarnTranslations';
10
10
  import type { LoginI18nInterface } from '@config/i18n/loginI18n';
11
11
  import { I } from '@config/IOCIdentifier';
12
+ import { ROUTE_REGISTER } from '@config/route';
12
13
 
13
14
  interface LoginFormData {
14
15
  email: string;
@@ -116,7 +117,7 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
116
117
 
117
118
  <div className="text-center mt-6">
118
119
  <span className="text-text-tertiary">{tt.noAccount} </span>
119
- <LocaleLink href="/register" title={tt.createAccountTitle}>
120
+ <LocaleLink href={ROUTE_REGISTER} title={tt.createAccountTitle}>
120
121
  {tt.createAccount}
121
122
  </LocaleLink>
122
123
  </div>
@@ -1,6 +1,6 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import type { PageParamsProps } from '@/base/types/AppPageRouter';
3
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
3
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
4
  import { redirect } from '@/i18n/routing';
5
5
  import {
6
6
  AppPageRouteParams,
@@ -11,6 +11,7 @@ import { FeatureItem } from '@/uikit/components/FeatureItem';
11
11
  import { AppRoutePage } from '@/uikit/components-app/AppRoutePage';
12
12
  import { loginI18n, i18nConfig, NS_PAGE_LOGIN } from '@config/i18n';
13
13
  import { COMMON_ADMIN_TITLE } from '@config/Identifier';
14
+ import { ROUTE_LOGIN } from '@config/route';
14
15
  import { LoginForm } from './LoginForm';
15
16
  import type { Metadata } from 'next';
16
17
 
@@ -45,9 +46,7 @@ export default async function LoginPage(props: PageParamsProps) {
45
46
  const params = await props.params;
46
47
  const pageParams = new AppPageRouteParams(params);
47
48
 
48
- const server = new BootstrapServer();
49
-
50
- if (await server.getIOC(ServerAuth).hasAuth()) {
49
+ if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
51
50
  return redirect({ href: '/', locale: params.locale! });
52
51
  }
53
52
 
@@ -63,7 +62,7 @@ export default async function LoginPage(props: PageParamsProps) {
63
62
  title: tt.title,
64
63
  adminTitle: tt.adminTitle
65
64
  }}
66
- headerHref="/login"
65
+ headerHref={ROUTE_LOGIN}
67
66
  mainProps={{
68
67
  className: 'text-xs1 bg-primary flex min-h-screen'
69
68
  }}
@@ -0,0 +1,8 @@
1
+ import { redirect } from 'next/navigation';
2
+ import { ROUTE_LOGIN } from '@config/route';
3
+
4
+ export default async function AuthRootPage() {
5
+ console.log('AuthRootPage redirect to login page');
6
+
7
+ redirect(ROUTE_LOGIN);
8
+ }
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { LockOutlined, MailOutlined, UserOutlined } from '@ant-design/icons';
4
+ import { useStore } from '@brain-toolkit/react-kit';
4
5
  import { Button, Checkbox, Form, Input } from 'antd';
5
6
  import { useState } from 'react';
6
7
  import { useIOC } from '@/uikit/hook/useIOC';
@@ -13,12 +14,20 @@ export function RegisterForm(props: { tt: RegisterI18nInterface }) {
13
14
  const logger = useIOC(I.Logger);
14
15
  const routerService = useIOC(I.RouterServiceInterface);
15
16
  const [loading, setLoading] = useState(false);
17
+ const result = useStore(userService.getUIStore(), (state) => state.result);
16
18
 
17
- const handleRegister = async (values: unknown) => {
19
+ const handleRegister = async (values: {
20
+ username: string;
21
+ email: string;
22
+ password: string;
23
+ }) => {
18
24
  try {
19
25
  setLoading(true);
20
- await userService.register(values);
21
- routerService.gotoLogin();
26
+ await userService.register({
27
+ username: values.username,
28
+ email: values.email,
29
+ password: values.password
30
+ });
22
31
  } catch (error) {
23
32
  logger.error(error);
24
33
  } finally {
@@ -39,7 +48,19 @@ export function RegisterForm(props: { tt: RegisterI18nInterface }) {
39
48
  layout="vertical"
40
49
  className="space-y-4"
41
50
  validateTrigger="onSubmit"
51
+ disabled={!!result}
52
+ initialValues={{
53
+ username: 'myused@sina.com',
54
+ email: 'myused@sina.com',
55
+ password: 'q1234566',
56
+ confirmPassword: 'q1234566',
57
+ agreeToTerms: true
58
+ }}
42
59
  >
60
+ {result && result.email_confirmed_at == null ? (
61
+ <div className="text-xl text-red-500">{tt.email_verify}</div>
62
+ ) : null}
63
+
43
64
  <Form.Item
44
65
  name="username"
45
66
  rules={[
@@ -1,6 +1,6 @@
1
1
  import { notFound } from 'next/navigation';
2
2
  import type { PageParamsProps } from '@/base/types/AppPageRouter';
3
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
3
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
4
  import { redirect } from '@/i18n/routing';
5
5
  import {
6
6
  AppPageRouteParams,
@@ -45,9 +45,7 @@ export default async function LoginPage(props: PageParamsProps) {
45
45
  const params = await props.params;
46
46
  const pageParams = new AppPageRouteParams(params);
47
47
 
48
- const server = new BootstrapServer();
49
-
50
- if (await server.getIOC(ServerAuth).hasAuth()) {
48
+ if (await bootstrapServer.getIOC(ServerAuth).hasAuth()) {
51
49
  return redirect({ href: '/', locale: params.locale! });
52
50
  }
53
51
 
@@ -63,7 +61,8 @@ export default async function LoginPage(props: PageParamsProps) {
63
61
  title: tt.title,
64
62
  adminTitle: tt.adminTitle
65
63
  }}
66
- headerHref="/login"
64
+ showAuthButton={false}
65
+ headerHref=""
67
66
  mainProps={{
68
67
  className: 'text-xs1 bg-primary flex min-h-screen'
69
68
  }}
@@ -1,14 +1,13 @@
1
1
  import { Button } from 'antd';
2
2
  import type { PageParamsProps } from '@/base/types/AppPageRouter';
3
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
- import { redirect } from '@/i18n/routing';
3
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
5
4
  import {
6
5
  AppPageRouteParams,
7
6
  type PageParamsType
8
7
  } from '@/server/AppPageRouteParams';
9
8
  import { ServerAuth } from '@/server/ServerAuth';
10
9
  import { AppRoutePage } from '@/uikit/components-app/AppRoutePage';
11
- import { i18nConfig, homeI18n } from '@config/i18n';
10
+ import { i18nConfig, homeI18n, homeI18nNamespace } from '@config/i18n';
12
11
  import { COMMON_ADMIN_TITLE } from '@config/Identifier';
13
12
  import type { Metadata } from 'next';
14
13
 
@@ -43,27 +42,27 @@ export async function generateMetadata({
43
42
  }
44
43
 
45
44
  export default async function Home({ params }: PageParamsProps) {
46
- const server = new BootstrapServer();
47
45
  const pageParams = new AppPageRouteParams(await params!);
48
- const locale = pageParams.getLocale();
49
- const tt = await pageParams.getI18nInterface({
50
- ...homeI18n,
51
- adminTitle: COMMON_ADMIN_TITLE
52
- });
46
+ const user = await bootstrapServer.getIOC(ServerAuth).getUser();
53
47
 
54
- if (!(await server.getIOC(ServerAuth).hasAuth())) {
55
- return redirect({ href: '/login', locale });
56
- }
48
+ // const locale = pageParams.getLocale();
49
+ const tt = await pageParams.getI18nInterface(
50
+ {
51
+ ...homeI18n,
52
+ adminTitle: COMMON_ADMIN_TITLE
53
+ },
54
+ homeI18nNamespace
55
+ );
57
56
 
58
57
  return (
59
58
  <AppRoutePage
60
59
  data-testid="AppRoute-HomePage"
61
- showLogoutButton
62
- showAdminButton
63
60
  tt={{
64
61
  title: tt.title,
65
62
  adminTitle: tt.adminTitle
66
63
  }}
64
+ showAdminButton
65
+ showAuthButton
67
66
  >
68
67
  {/* Hero Section */}
69
68
  <section className="py-16 px-4">
@@ -71,41 +70,15 @@ export default async function Home({ params }: PageParamsProps) {
71
70
  <h1 className="text-4xl md:text-5xl font-bold mb-6 text-text">
72
71
  {tt.welcome}
73
72
  </h1>
73
+ {!!user ? (
74
+ <p data-testid="AuthUserEmail" className="text-lg text-text">
75
+ {user.email}
76
+ </p>
77
+ ) : null}
74
78
  <p className="text-xl text-text-secondary mb-8">{tt.description}</p>
75
79
  </div>
76
80
  </section>
77
81
 
78
- {/* Navigation Grid */}
79
- <section className="max-w-6xl mx-auto px-4 py-12">
80
- <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
81
- {/* {navigationItems.map((item) => (
82
- <LocaleLink
83
- data-testid={`HomePage-navigation-${item.href}`}
84
- key={item.href}
85
- title={item.titleKey}
86
- className={clsx(
87
- href={item.href}
88
- 'block rounded-lg p-6',
89
- 'bg-secondary',
90
- 'border border-border',
91
- 'hover:bg-elevated',
92
- 'transition-colors duration-200'
93
- )}
94
- >
95
- <h3 className={`text-xl font-semibold mb-3 text-text`}>
96
- {t(item.titleKey)}
97
- </h3>
98
- <p className="text-text-secondary mb-4">
99
- {t(item.descriptionKey)}
100
- </p>
101
- <Button type="primary" className="w-full">
102
- {t(i18nKeys.HOME_EXPLORE)}
103
- </Button>
104
- </LocaleLink>
105
- ))} */}
106
- </div>
107
- </section>
108
-
109
82
  {/* Call to Action Section */}
110
83
  <section className="py-16 px-4 bg-elevated">
111
84
  <div className="max-w-4xl mx-auto text-center">
@@ -1,25 +1,25 @@
1
1
  import { ExecutorError } from '@qlover/fe-corekit';
2
2
  import { NextResponse } from 'next/server';
3
- import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
3
+ import { bootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
4
  import { AppErrorApi } from '@/server/AppErrorApi';
5
5
  import { AppSuccessApi } from '@/server/AppSuccessApi';
6
6
  import { AIService } from '@/server/services/AIService';
7
7
 
8
8
  export async function GET() {
9
- const server = new BootstrapServer();
9
+ const result = await bootstrapServer.execNoError(
10
+ async ({ parameters: { IOC } }) => {
11
+ // const requestBody = await req.json();
10
12
 
11
- const result = await server.execNoError(async ({ parameters: { IOC } }) => {
12
- // const requestBody = await req.json();
13
+ const result = await IOC(AIService).completions([
14
+ {
15
+ role: 'user',
16
+ content: 'hello'
17
+ }
18
+ ]);
13
19
 
14
- const result = await IOC(AIService).completions([
15
- {
16
- role: 'user',
17
- content: 'hello'
18
- }
19
- ]);
20
-
21
- return result;
22
- });
20
+ return result;
21
+ }
22
+ );
23
23
 
24
24
  if (result instanceof ExecutorError) {
25
25
  console.log(result);
@@ -0,0 +1,11 @@
1
+ import { NextResponse } from 'next/server';
2
+ import type { NextRequest } from 'next/server';
3
+
4
+ export function GET(req: NextRequest) {
5
+ return NextResponse.json({
6
+ message: 'Hello World',
7
+ hash: req.nextUrl.hash,
8
+ url: req.url,
9
+ href: req.nextUrl.href
10
+ });
11
+ }
@@ -0,0 +1,49 @@
1
+ import { type NextRequest, NextResponse } from 'next/server';
2
+ import { createClient } from '@/lib/supabase/server';
3
+
4
+ export async function GET(request: NextRequest) {
5
+ const requestUrl = new URL(request.url);
6
+ const code = requestUrl.searchParams.get('code');
7
+
8
+ if (code) {
9
+ console.log('api/callback code', code);
10
+ try {
11
+ const supabase = await createClient();
12
+ // 使用 code 交换 session
13
+ const { error } = await supabase.auth.exchangeCodeForSession(code);
14
+
15
+ if (error) {
16
+ console.error('验证失败:', error.message);
17
+ // 重定向到错误页面
18
+ return NextResponse.redirect(
19
+ new URL('/auth/error?error=verification_failed', request.url)
20
+ );
21
+ }
22
+
23
+ // 获取用户信息
24
+ const {
25
+ data: { user }
26
+ } = await supabase.auth.getUser();
27
+
28
+ // 检查邮箱是否已验证
29
+ if (user && user.email_confirmed_at) {
30
+ console.log('✅ 邮箱已验证成功');
31
+ // 重定向到成功页面
32
+ return NextResponse.redirect(new URL('/', request.url));
33
+ } else {
34
+ console.log('⏳ 邮箱验证中...');
35
+ // 重定向到验证提醒页面
36
+ return NextResponse.redirect(
37
+ new URL('/auth/verify-email', request.url)
38
+ );
39
+ }
40
+ } catch (error) {
41
+ console.error('回调处理错误:', error);
42
+ return NextResponse.redirect(new URL('/auth/error', request.url));
43
+ }
44
+ }
45
+
46
+ // 如果没有 code 参
47
+ // 数,重定向到登录页
48
+ return NextResponse.redirect(new URL('/auth/login', request.url));
49
+ }
@@ -35,4 +35,6 @@ export class AppConfig implements EnvConfigInterface {
35
35
 
36
36
  public readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
37
37
  public readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
38
+
39
+ public readonly isProduction: boolean = this.env === 'production';
38
40
  }