@qlover/create-app 0.7.13 → 0.7.14

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 (66) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/config/IOCIdentifier.ts +14 -1
  5. package/dist/templates/next-app/config/Identifier/index.ts +1 -0
  6. package/dist/templates/next-app/config/Identifier/page.admin.ts +48 -0
  7. package/dist/templates/next-app/config/i18n/admin18n.ts +33 -0
  8. package/dist/templates/next-app/config/i18n/index.ts +3 -1
  9. package/dist/templates/next-app/migrations/schema/UserSchema.ts +2 -2
  10. package/dist/templates/next-app/next.config.ts +1 -1
  11. package/dist/templates/next-app/package.json +3 -1
  12. package/dist/templates/next-app/public/locales/en.json +8 -1
  13. package/dist/templates/next-app/public/locales/zh.json +8 -1
  14. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +1 -1
  15. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +14 -16
  16. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +10 -3
  17. package/dist/templates/next-app/src/app/[locale]/layout.tsx +1 -1
  18. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  19. package/dist/templates/next-app/src/app/[locale]/page.tsx +2 -3
  20. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  21. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +32 -0
  22. package/dist/templates/next-app/src/base/cases/AppConfig.ts +3 -0
  23. package/dist/templates/next-app/src/base/cases/ChatAction.ts +21 -0
  24. package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +36 -0
  25. package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +1 -3
  26. package/dist/templates/next-app/src/base/services/AdminUserService.ts +1 -1
  27. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +1 -1
  28. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +23 -1
  29. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +2 -2
  30. package/dist/templates/next-app/src/base/types/PageProps.ts +1 -1
  31. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +1 -0
  32. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +1 -0
  33. package/dist/templates/next-app/src/core/globals.ts +2 -0
  34. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +4 -1
  35. package/dist/templates/next-app/src/{base/cases → server}/PageParams.ts +1 -1
  36. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +31 -0
  37. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +1 -1
  38. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +6 -4
  39. package/dist/templates/next-app/src/server/services/AIService.ts +43 -0
  40. package/dist/templates/next-app/src/server/services/ApiUserService.ts +1 -1
  41. package/dist/templates/next-app/src/server/{SupabaseBridge.ts → sqlBridges/SupabaseBridge.ts} +16 -11
  42. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +4 -4
  43. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +1 -1
  44. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +32 -25
  45. package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +12 -26
  46. package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +37 -5
  47. package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +17 -0
  48. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +36 -0
  49. package/dist/templates/next-app/src/uikit/components/LanguageSwitcher.tsx +5 -6
  50. package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +2 -0
  51. package/dist/templates/next-app/src/uikit/components/With.tsx +17 -0
  52. package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +30 -0
  53. package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +65 -0
  54. package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +59 -0
  55. package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +28 -0
  56. package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +19 -0
  57. package/package.json +1 -1
  58. package/dist/templates/next-app/src/base/port/DBBridgeInterface.ts +0 -21
  59. package/dist/templates/next-app/src/base/port/DBMigrationInterface.ts +0 -92
  60. package/dist/templates/next-app/src/base/port/MigrationApiInterface.ts +0 -3
  61. package/dist/templates/next-app/src/base/port/ServerApiResponseInterface.ts +0 -6
  62. package/dist/templates/next-app/src/base/services/migrations/MigrationsApi.ts +0 -43
  63. package/dist/templates/next-app/config/i18n/{HomeI18n .ts → HomeI18n.ts} +0 -0
  64. package/dist/templates/next-app/{build → make}/generateLocales.ts +2 -2
  65. /package/dist/templates/next-app/src/{base → server}/port/PaginationInterface.ts +0 -0
  66. /package/dist/templates/next-app/src/{base → server}/port/ParamsHandlerInterface.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 0.7.14
4
+
5
+ ### Patch Changes
6
+
7
+ #### ✨ Features
8
+
9
+ - **next-app:** integrate SupabaseBridge and update database handling ([5d84568](https://github.com/qlover/fe-base/commit/5d84568849170c998c81b663f0eb5f5f144d8da3)) ([#518](https://github.com/qlover/fe-base/pull/518))
10
+ - Added SupabaseBridge for managing database interactions, implementing the DBBridgeInterface for standardized operations.
11
+ - Updated UserRepository to utilize the new DBBridgeInterface for database operations.
12
+ - Enhanced AppConfig to switch OpenAI configuration to Cerebras.
13
+ - Refactored UserSchema validation to improve type safety with Zod.
14
+ - Introduced new pagination handling in DBBridgeInterface for better data management.
15
+
16
+ These changes aim to enhance database management capabilities and improve the overall structure of data handling within the application.
17
+
18
+ - **next-app:** add HomeI18n for localization and enhance layout components ([390a637](https://github.com/qlover/fe-base/commit/390a6375b458c57ca69517d3e4cf1b938f7b4aea)) ([#518](https://github.com/qlover/fe-base/pull/518))
19
+ - Introduced HomeI18n interface and constants for home page localization, improving internationalization support.
20
+ - Updated index.ts to export HomeI18n for easier access across the application.
21
+ - Refactored page.tsx to integrate homeI18n, enhancing metadata handling for the home page.
22
+ - Improved AdminLayout and BaseHeader components by adding right action buttons for language switching, theme toggling, and logout functionality.
23
+ - Enhanced BaseLayout to conditionally render admin navigation elements, improving user experience in the admin section.
24
+
25
+ These changes aim to provide better localization support and enhance the overall structure and usability of the application.
26
+
27
+ - **next-app:** add admin page localization and SEO components ([d8ca688](https://github.com/qlover/fe-base/commit/d8ca68861c5b2dc80763622b2f37de6ea0cfb437)) ([#518](https://github.com/qlover/fe-base/pull/518))
28
+ - Introduced new localization constants for the admin page, enhancing internationalization support.
29
+ - Created admin18n interface for managing admin page metadata and content.
30
+ - Added ClientSeo component for improved SEO handling on the admin page.
31
+ - Updated admin page structure to utilize new localization and SEO features, enhancing user experience.
32
+ - Refactored localization files to include new keys for admin page content in both English and Chinese.
33
+
34
+ These changes aim to improve the admin page's usability and visibility through better localization and SEO practices.
35
+
36
+ #### 🐞 Bug Fixes
37
+
38
+ - **create-app:** change build to make ([ea48d14](https://github.com/qlover/fe-base/commit/ea48d140e6f7684efc2e3097046bce82b6448d14)) ([#518](https://github.com/qlover/fe-base/pull/518))
39
+
40
+ #### ♻️ Refactors
41
+
42
+ - **next-app:** remove i18nService dependency from layout components ([c4647fa](https://github.com/qlover/fe-base/commit/c4647fa3a51a72354a70e200dd92439d64548fa2)) ([#520](https://github.com/qlover/fe-base/pull/520))
43
+ - Eliminated the i18nService dependency from AdminLayout and BaseLayout components to simplify the code structure.
44
+ - Updated LanguageSwitcher to directly use the i18nService via IOC, enhancing encapsulation and reducing prop drilling.
45
+ - Adjusted memoization dependencies in right action buttons to reflect the removal of i18nService.
46
+
47
+ These changes aim to streamline the layout components and improve maintainability by reducing unnecessary dependencies.
48
+
49
+ - **next-app:** reorganize imports and migrate PageParams to server ([fb6a47b](https://github.com/qlover/fe-base/commit/fb6a47b5b3a293b7d3e8488690e13b1509e9cfde)) ([#518](https://github.com/qlover/fe-base/pull/518))
50
+ - Updated import paths for PageParams and DBBridgeInterface to reflect new server structure.
51
+ - Removed obsolete migration-related interfaces and classes to streamline the codebase.
52
+ - Introduced new PageParams class in the server directory to handle localization and parameter management.
53
+
54
+ These changes aim to enhance code organization and improve the clarity of parameter handling within the application.
55
+
56
+ - **next-app:** update PaginationInterface imports and add new file ([dfd9866](https://github.com/qlover/fe-base/commit/dfd98660ea51989e604cb7d78d935590473bc05b)) ([#518](https://github.com/qlover/fe-base/pull/518))
57
+ - Changed import paths for PaginationInterface across multiple files to reflect its new location in the server directory.
58
+ - Introduced a new PaginationInterface file in the server port directory, defining the structure for pagination handling.
59
+
60
+ These changes aim to improve code organization and maintainability by centralizing pagination-related definitions.
61
+
62
+ - **next-app:** reorganize imports and update dependencies ([a1049f4](https://github.com/qlover/fe-base/commit/a1049f4bd61b012ca1938c803bdc3a4c43ef0c9a)) ([#518](https://github.com/qlover/fe-base/pull/518))
63
+ - Removed duplicate import statements in generateLocales.ts for cleaner code.
64
+ - Updated useEffect dependency array in UsersPage to include adminUserService, ensuring proper initialization.
65
+ - Cleaned up globals.ts by removing unnecessary comments, enhancing readability.
66
+
67
+ These changes aim to improve code organization and maintainability across the application.
68
+
3
69
  ## 0.7.13
4
70
 
5
71
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${t}`,_n=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),mn=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Ye(D,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#D.frames.length,this.#c=t);let{frames:r}=this.#D,u=r[this.#i];this.color&&(u=d[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",D=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+D+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#n;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#n=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&$e.hide(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&(this.#r=!0,Je.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&$e.show(this.#u),this.#t.discardStdin&&V.default.stdin.isTTY&&this.#r&&(Je.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:H.success,text:t})}fail(t){return this.stopAndPersist({symbol:H.error,text:t})}warn(t){return this.stopAndPersist({symbol:H.warning,text:t})}info(t){return this.stopAndPersist({symbol:H.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function rr(e){return new Ze(e)}async function ur(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:D}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=rr(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}var bt=require("fs");var P=require("path"),me=require("fs"),fr=h(cr(),1),Dt=require("fs");var de=require("fs"),X=class{static ensureDir(t){(0,de.existsSync)(t)||(0,de.mkdirSync)(t,{recursive:!0})}};var{copyFile:us,stat:is}=Dt.promises,_e=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=(0,P.join)(t,this.ignoreFile);if(!(0,me.existsSync)(r))return;let D=(0,me.readFileSync)(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.7.13",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 Wl(){let e=new YD.Command;return e.version(vt.version,"-v, --version","Show version").description(vt.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function VD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Wl(),i=(0,xt.resolve)(e,"./templates"),D=(0,xt.resolve)(e,"./configs");(0,yt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,yt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}VD(__dirname).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,fr.default)().add(D)}async copyFiles(t,r,u,i){let D=await Dt.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,P.join)(t,o),a=(0,P.join)(r,o);if(u&&u.ignores(o))return;if(X.ensureDir((0,P.dirname)(a)),(await is(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await us(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){X.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var L=require("fs"),$D=h(kD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,L.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,L.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,$D.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,L.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var UD=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=ur,this.context=new WD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new _e((0,v.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await HD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return UD.includes(t)}async getGeneratorContext(){let t=wt(this.subPackages,UD),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=Pt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,v.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,v.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,v.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,v.join)(D,o),a=(0,v.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var vt={name:"@qlover/create-app",version:"0.7.14",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 Wl(){let e=new YD.Command;return e.version(vt.version,"-v, --version","Show version").description(vt.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function VD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Wl(),i=(0,xt.resolve)(e,"./templates"),D=(0,xt.resolve)(e,"./configs");(0,yt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,yt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}VD(__dirname).catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,pD=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),hD=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(n,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#n.frames.length,this.#c=t);let{frames:r}=this.#n,u=r[this.#i];this.color&&(u=E[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",n=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+n+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#D;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#D=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#D=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&je.hide(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&(this.#r=!0,We.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&je.show(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&this.#r&&(We.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:k.success,text:t})}fail(t){return this.stopAndPersist({symbol:k.error,text:t})}warn(t){return this.stopAndPersist({symbol:k.warning,text:t})}info(t){return this.stopAndPersist({symbol:k.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",n=t.text??this.text,s=typeof n=="string"?(i?" ":"")+n:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function zt(e){return new He(e)}async function Kt(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:n}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=zt(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(n===void 0?void 0:typeof n=="string"?n:n(s)),s}}import{existsSync as Nn}from"fs";var nr=ue(ir(),1);import{dirname as QD,join as Ze}from"path";import{existsSync as es,readFileSync as ts}from"fs";import{promises as Dr}from"fs";import{existsSync as JD,mkdirSync as ZD}from"fs";var H=class{static ensureDir(t){JD(t)||ZD(t,{recursive:!0})}};var{copyFile:rs,stat:us}=Dr,Ce=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=Ze(t,this.ignoreFile);if(!es(r))return;let n=ts(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.7.13",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 Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,nr.default)().add(n)}async copyFiles(t,r,u,i){let n=await Dr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.7.14",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 Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
@@ -1,9 +1,9 @@
1
1
  import type { AppConfig } from '@/base/cases/AppConfig';
2
2
  import type { DialogHandler } from '@/base/cases/DialogHandler';
3
3
  import type { RouterService } from '@/base/cases/RouterService';
4
- import type { DBBridgeInterface } from '@/base/port/DBBridgeInterface';
5
4
  import type { I18nService } from '@/base/services/I18nService';
6
5
  import type { UserService } from '@/base/services/UserService';
6
+ import type { DBBridgeInterface } from '@/server/port/DBBridgeInterface';
7
7
  import type * as CorekitBridge from '@qlover/corekit-bridge';
8
8
  import type * as FeCorekit from '@qlover/fe-corekit';
9
9
  import type { LoggerInterface } from '@qlover/logger';
@@ -22,6 +22,19 @@ export const IOCIdentifier = Object.freeze({
22
22
  UserServiceInterface: 'UserServiceInterface',
23
23
  RouterServiceInterface: 'RouterServiceInterface',
24
24
  I18nServiceInterface: 'I18nServiceInterface',
25
+ /**
26
+ * 数据库桥接接口
27
+ *
28
+ * 你可以实现不同的例如:
29
+ *
30
+ * - Vercel Postgres
31
+ * - supabase
32
+ * - mysql
33
+ * - postgresql
34
+ * - mongodb
35
+ * - redis
36
+ * - sqllite
37
+ */
25
38
  DBBridgeInterface: 'DBBridgeInterface'
26
39
  });
27
40
 
@@ -2,6 +2,7 @@ export * from './api';
2
2
  export * from './common';
3
3
  export * from './common.error';
4
4
  export * from './page.about';
5
+ export * from './page.admin';
5
6
  export * from './page.executor';
6
7
  export * from './page.home';
7
8
  export * from './page.identifiter';
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @description Admin page title
3
+ * @localZh 管理员页面
4
+ * @localEn Admin page
5
+ */
6
+ export const PAGE_ADMIN_TITLE = 'page__admin__title';
7
+
8
+ /**
9
+ * @description Admin page description
10
+ * @localZh 管理员页面
11
+ * @localEn Admin page
12
+ */
13
+ export const PAGE_ADMIN_DESCRIPTION = 'page__admin__description';
14
+
15
+ /**
16
+ * @description Admin page keywords
17
+ * @localZh 管理员页面关键词
18
+ * @localEn Admin page
19
+ */
20
+ export const PAGE_ADMIN_KEYWORDS = 'page__admin__keywords';
21
+
22
+ /**
23
+ * @description Home page welcome message
24
+ * @localZh 欢迎来到管理员页面
25
+ * @localEn Welcome to the admin page
26
+ */
27
+ export const ADMIN_WELCOME = 'admin__welcome';
28
+
29
+ /**
30
+ * @description Admin users page title
31
+ * @localZh 后台管理 - 管理员用户页面
32
+ * @localEn Admin users page
33
+ */
34
+ export const PAGE_ADMIN_USERS_TITLE = 'page__admin__users__title';
35
+
36
+ /**
37
+ * @description Admin users page description
38
+ * @localZh 后台管理 - 管理员用户页面
39
+ * @localEn Admin users page
40
+ */
41
+ export const PAGE_ADMIN_USERS_DESCRIPTION = 'page__admin__users__description';
42
+
43
+ /**
44
+ * @description Admin users page keywords
45
+ * @localZh 后台管理 - 管理员用户页面关键词
46
+ * @localEn Admin users page keywords
47
+ */
48
+ export const PAGE_ADMIN_USERS_KEYWORDS = 'page__admin__users__keywords';
@@ -0,0 +1,33 @@
1
+ import * as i18nKeys from '../Identifier/page.admin';
2
+
3
+ /**
4
+ * Register page i18n interface
5
+ *
6
+ * @description
7
+ * - welcome: welcome message
8
+ */
9
+ export type AdminI18nInterface = typeof admin18n;
10
+
11
+ export const admin18n = Object.freeze({
12
+ // basic meta properties
13
+ title: i18nKeys.PAGE_ADMIN_TITLE,
14
+ description: i18nKeys.PAGE_ADMIN_DESCRIPTION,
15
+ content: i18nKeys.PAGE_ADMIN_DESCRIPTION,
16
+ keywords: i18nKeys.PAGE_ADMIN_KEYWORDS,
17
+
18
+ // admin page
19
+ welcome: i18nKeys.ADMIN_WELCOME
20
+ });
21
+
22
+ export type AdminUsersI18nInterface = typeof adminUsers18n;
23
+
24
+ export const adminUsers18n = Object.freeze({
25
+ // basic meta properties
26
+ title: i18nKeys.PAGE_ADMIN_USERS_TITLE,
27
+ description: i18nKeys.PAGE_ADMIN_USERS_DESCRIPTION,
28
+ content: i18nKeys.PAGE_ADMIN_USERS_DESCRIPTION,
29
+ keywords: i18nKeys.PAGE_ADMIN_USERS_KEYWORDS,
30
+
31
+ // admin page
32
+ welcome: i18nKeys.ADMIN_WELCOME
33
+ });
@@ -1,4 +1,6 @@
1
+ export * from './HomeI18n';
2
+ export * from './admin18n';
1
3
  export * from './i18nConfig';
2
- export * from './PageI18nInterface';
3
4
  export * from './loginI18n';
5
+ export * from './PageI18nInterface';
4
6
  export * from './register18n';
@@ -9,8 +9,8 @@ export type UserRoleType = (typeof UserRole)[keyof typeof UserRole];
9
9
 
10
10
  export const userSchema = z.object({
11
11
  id: z.number(),
12
- role: z.enum(UserRole),
13
- email: z.email(),
12
+ role: z.nativeEnum(UserRole),
13
+ email: z.string().email(),
14
14
  password: z.string(),
15
15
  /**
16
16
  * 加密的token, 包含token, 过期时间
@@ -1,5 +1,5 @@
1
1
  import createNextIntlPlugin from 'next-intl/plugin';
2
- import { generateLocales } from './build/generateLocales';
2
+ import { generateLocales } from './make/generateLocales';
3
3
  import type { NextConfig } from 'next';
4
4
 
5
5
  const withNextIntl = createNextIntlPlugin();
@@ -37,9 +37,11 @@
37
37
  "next": "15.5.0",
38
38
  "next-intl": "^4.3.5",
39
39
  "next-themes": "^0.4.6",
40
+ "openai": "^5.23.0",
40
41
  "react": "19.1.0",
41
42
  "react-dom": "19.1.0",
42
- "zod": "^4.1.8"
43
+ "reflect-metadata": "^0.2.2",
44
+ "zod": "^3.23.8"
43
45
  },
44
46
  "devDependencies": {
45
47
  "@brain-toolkit/ts2locales": "^0.2.3",
@@ -198,5 +198,12 @@
198
198
  "register__feature__progress_tracking": "Progress Tracking",
199
199
  "page__head__admin__title": "Admin Backend",
200
200
  "api__not_authorized": "Not authorized",
201
- "api__page__invalid": "Page number is incorrect"
201
+ "api__page__invalid": "Page number is incorrect",
202
+ "page__admin__title": "Admin page",
203
+ "page__admin__description": "Admin page",
204
+ "page__admin__keywords": "Admin page",
205
+ "admin__welcome": "Welcome to the admin page",
206
+ "page__admin__users__title": "Admin users page",
207
+ "page__admin__users__description": "Admin users page",
208
+ "page__admin__users__keywords": "Admin users page keywords"
202
209
  }
@@ -198,5 +198,12 @@
198
198
  "register__feature__progress_tracking": "进度跟踪",
199
199
  "page__head__admin__title": "管理后台",
200
200
  "api__not_authorized": "未授权",
201
- "api__page__invalid": "页码不正确"
201
+ "api__page__invalid": "页码不正确",
202
+ "page__admin__title": "管理员页面",
203
+ "page__admin__description": "管理员页面",
204
+ "page__admin__keywords": "管理员页面关键词",
205
+ "admin__welcome": "欢迎来到管理员页面",
206
+ "page__admin__users__title": "后台管理 - 管理员用户页面",
207
+ "page__admin__users__description": "后台管理 - 管理员用户页面",
208
+ "page__admin__users__keywords": "后台管理 - 管理员用户页面关键词"
202
209
  }
@@ -1,5 +1,5 @@
1
- import { PageParams } from '@/base/cases/PageParams';
2
1
  import type { PageLayoutProps } from '@/base/types/PageProps';
2
+ import { PageParams } from '@/server/PageParams';
3
3
  import '@/styles/css/index.css';
4
4
  import { AdminLayout } from '@/uikit/components/AdminLayout';
5
5
 
@@ -1,22 +1,20 @@
1
- import type { Metadata } from 'next';
1
+ 'use client';
2
2
 
3
- function AdminPageClient() {
4
- return <div data-testid="AdminPage">Admin Page</div>;
5
- }
6
-
7
- export const metadata: Metadata = {
8
- title: 'Admin Dashboard',
9
- description: 'Admin dashboard for managing application resources',
10
- robots: {
11
- index: false,
12
- follow: false
13
- }
14
- };
3
+ import { ClientSeo } from '@/uikit/components/ClientSeo';
4
+ import { useI18nInterface } from '@/uikit/hook/useI18nInterface';
5
+ import { admin18n } from '@config/i18n';
15
6
 
16
7
  export default function AdminPage() {
8
+ const tt = useI18nInterface(admin18n);
9
+
17
10
  return (
18
- <div data-testid="AdminPageWrapper">
19
- <AdminPageClient />
20
- </div>
11
+ <>
12
+ <ClientSeo i18nInterface={tt} />
13
+ <div data-testid="AdminPageWrapper">
14
+ <div data-testid="AdminPage">
15
+ <h1>{tt.welcome}</h1>
16
+ </div>
17
+ </div>
18
+ </>
21
19
  );
22
20
  }
@@ -3,9 +3,12 @@
3
3
  import { Table } from 'antd';
4
4
  import { useEffect, useRef } from 'react';
5
5
  import { AdminUserService } from '@/base/services/AdminUserService';
6
+ import { ClientSeo } from '@/uikit/components/ClientSeo';
7
+ import { useI18nInterface } from '@/uikit/hook/useI18nInterface';
6
8
  import { useIOC } from '@/uikit/hook/useIOC';
7
9
  import { useStore } from '@/uikit/hook/useStore';
8
10
  import { userSchema, type UserSchema } from '@migrations/schema/UserSchema';
11
+ import { adminUsers18n } from '@config/i18n';
9
12
  import type { ColumnsType } from 'antd/es/table';
10
13
 
11
14
  const baseColumns: ColumnsType<UserSchema> = Object.keys(
@@ -20,7 +23,7 @@ const baseColumns: ColumnsType<UserSchema> = Object.keys(
20
23
 
21
24
  export default function UsersPage() {
22
25
  const adminUserService = useIOC(AdminUserService);
23
-
26
+ const tt = useI18nInterface(adminUsers18n);
24
27
  const listParams = useStore(adminUserService, (state) => state.listParams);
25
28
  const listState = useStore(adminUserService, (state) => state.listState);
26
29
  const mouted = useRef(false);
@@ -28,9 +31,12 @@ export default function UsersPage() {
28
31
  useEffect(() => {
29
32
  if (!mouted.current) {
30
33
  mouted.current = true;
31
- adminUserService.initialize();
34
+
35
+ requestAnimationFrame(() => {
36
+ adminUserService.initialize();
37
+ });
32
38
  }
33
- }, []);
39
+ }, [adminUserService]);
34
40
 
35
41
  const dataSource = listState.result?.list as UserSchema[];
36
42
 
@@ -38,6 +44,7 @@ export default function UsersPage() {
38
44
 
39
45
  return (
40
46
  <div data-testid="UsersPage">
47
+ <ClientSeo i18nInterface={tt} />
41
48
  <Table
42
49
  rowKey="id"
43
50
  columns={columns}
@@ -1,6 +1,6 @@
1
1
  import { NextIntlClientProvider } from 'next-intl';
2
- import { PageParams } from '@/base/cases/PageParams';
3
2
  import type { PageLayoutProps } from '@/base/types/PageProps';
3
+ import { PageParams } from '@/server/PageParams';
4
4
  import { ComboProvider } from '@/uikit/components/ComboProvider';
5
5
  import { themeConfig } from '@config/theme';
6
6
  import '@/styles/css/index.css';
@@ -1,8 +1,8 @@
1
1
  import { notFound } from 'next/navigation';
2
- import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
3
2
  import type { PageParamsProps } from '@/base/types/PageProps';
4
3
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
5
4
  import { redirect } from '@/i18n/routing';
5
+ import { PageParams, type PageParamsType } from '@/server/PageParams';
6
6
  import { ServerAuth } from '@/server/ServerAuth';
7
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
8
8
  import { FeatureItem } from '@/uikit/components/FeatureItem';
@@ -1,12 +1,11 @@
1
1
  import { Button } from 'antd';
2
- import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
3
2
  import type { PageParamsProps } from '@/base/types/PageProps';
4
3
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
5
4
  import { redirect } from '@/i18n/routing';
5
+ import { PageParams, type PageParamsType } from '@/server/PageParams';
6
6
  import { ServerAuth } from '@/server/ServerAuth';
7
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
8
- import { i18nConfig } from '@config/i18n';
9
- import { homeI18n } from '@config/i18n/HomeI18n ';
8
+ import { i18nConfig, homeI18n } from '@config/i18n';
10
9
  import type { Metadata } from 'next';
11
10
 
12
11
  // const navigationItems = [
@@ -1,8 +1,8 @@
1
1
  import { notFound } from 'next/navigation';
2
- import { PageParams, type PageParamsType } from '@/base/cases/PageParams';
3
2
  import type { PageParamsProps } from '@/base/types/PageProps';
4
3
  import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
5
4
  import { redirect } from '@/i18n/routing';
5
+ import { PageParams, type PageParamsType } from '@/server/PageParams';
6
6
  import { ServerAuth } from '@/server/ServerAuth';
7
7
  import { BaseLayout } from '@/uikit/components/BaseLayout';
8
8
  import { FeatureItem } from '@/uikit/components/FeatureItem';
@@ -0,0 +1,32 @@
1
+ import { ExecutorError } from '@qlover/fe-corekit';
2
+ import { NextResponse } from 'next/server';
3
+ import { BootstrapServer } from '@/core/bootstraps/BootstrapServer';
4
+ import { AppErrorApi } from '@/server/AppErrorApi';
5
+ import { AppSuccessApi } from '@/server/AppSuccessApi';
6
+ import { AIService } from '@/server/services/AIService';
7
+
8
+ export async function GET() {
9
+ const server = new BootstrapServer();
10
+
11
+ const result = await server.execNoError(async ({ parameters: { IOC } }) => {
12
+ // const requestBody = await req.json();
13
+
14
+ const result = await IOC(AIService).completions([
15
+ {
16
+ role: 'user',
17
+ content: 'hello'
18
+ }
19
+ ]);
20
+
21
+ return result;
22
+ });
23
+
24
+ if (result instanceof ExecutorError) {
25
+ console.log(result);
26
+ return NextResponse.json(new AppErrorApi(result.id, result.message), {
27
+ status: 400
28
+ });
29
+ }
30
+
31
+ return NextResponse.json(new AppSuccessApi(result));
32
+ }
@@ -31,4 +31,7 @@ export class AppConfig implements EnvConfigInterface {
31
31
  * @example '1 year'
32
32
  */
33
33
  readonly jwtExpiresIn: StringValue = '30 days';
34
+
35
+ readonly openaiBaseUrl: string = process.env.CEREBRAS_BASE_URL!;
36
+ readonly openaiApiKey: string = process.env.CEREBRAS_API_KEY!;
34
37
  }
@@ -0,0 +1,21 @@
1
+ import { injectable } from 'inversify';
2
+ import {
3
+ ChatActionInterface,
4
+ type MessageInterface,
5
+ type ChatStateInterface
6
+ } from '@/uikit/components/chat/ChatActionInterface';
7
+
8
+ class ChatState implements ChatStateInterface {
9
+ messages: MessageInterface[] = [];
10
+ }
11
+
12
+ @injectable()
13
+ export class ChatAction extends ChatActionInterface<ChatStateInterface> {
14
+ constructor() {
15
+ super(() => new ChatState());
16
+ }
17
+
18
+ focus(): void {
19
+ console.log('focus');
20
+ }
21
+ }
@@ -0,0 +1,36 @@
1
+ import { injectable } from 'inversify';
2
+
3
+ import type { FocusBarStateInterface } from '@/uikit/components/chat/FocusBarActionInterface';
4
+ import { FocusBarActionInterface } from '@/uikit/components/chat/FocusBarActionInterface';
5
+ import { RequestState } from './RequestState';
6
+
7
+ class FocusBarState implements FocusBarStateInterface {
8
+ showHistoryArea = false;
9
+ inputValue = '';
10
+ sendState = new RequestState();
11
+ }
12
+
13
+ @injectable()
14
+ export class FocusBarAction extends FocusBarActionInterface<FocusBarStateInterface> {
15
+ constructor() {
16
+ super(() => new FocusBarState());
17
+ }
18
+
19
+ sendMessage(_message: string): Promise<void> {
20
+ return new Promise((resolve) => {
21
+ this.emit(this.cloneState({ sendState: new RequestState(true) }));
22
+ setTimeout(() => {
23
+ this.emit(this.cloneState({ sendState: new RequestState().end() }));
24
+ resolve();
25
+ }, 1000);
26
+ });
27
+ }
28
+
29
+ setInputValue(value: string): void {
30
+ this.emit(this.cloneState({ inputValue: value }));
31
+ }
32
+
33
+ clearInput(): void {
34
+ this.emit(this.cloneState({ inputValue: '' }));
35
+ }
36
+ }
@@ -1,6 +1,6 @@
1
1
  import { StoreInterface } from '@qlover/corekit-bridge';
2
2
  import { RequestState } from '../cases/RequestState';
3
- import type { PaginationInterface } from './PaginationInterface';
3
+ import type { PaginationInterface } from '../../server/port/PaginationInterface';
4
4
  import type { StoreStateInterface } from '@qlover/corekit-bridge';
5
5
 
6
6
  export interface AdminPageListParams {
@@ -34,8 +34,6 @@ export abstract class AdminPageInterface<
34
34
  try {
35
35
  const result = await this.fetchList(this.state.listParams);
36
36
 
37
- console.log('jj result', result);
38
-
39
37
  this.emit(
40
38
  this.cloneState({
41
39
  initState: new RequestState(false, result).end()
@@ -1,4 +1,5 @@
1
1
  import { inject, injectable } from 'inversify';
2
+ import type { PaginationInterface } from '@/server/port/PaginationInterface';
2
3
  import {
3
4
  AdminPageInterface,
4
5
  type AdminPageListParams,
@@ -6,7 +7,6 @@ import {
6
7
  } from '../port/AdminPageInterface';
7
8
  import { AdminUserApi } from './adminApi/AdminUserApi';
8
9
  import { RequestState } from '../cases/RequestState';
9
- import type { PaginationInterface } from '../port/PaginationInterface';
10
10
 
11
11
  @injectable()
12
12
  export class AdminUserService extends AdminPageInterface<AdminPageState> {
@@ -1,6 +1,6 @@
1
1
  import { inject, injectable } from 'inversify';
2
2
  import type { AdminPageListParams } from '@/base/port/AdminPageInterface';
3
- import type { PaginationInterface } from '@/base/port/PaginationInterface';
3
+ import type { PaginationInterface } from '@/server/port/PaginationInterface';
4
4
  import {
5
5
  AppApiRequester,
6
6
  type AppApiConfig,