diff --git a/CHANGELOG.md b/CHANGELOG.md index 0747de848b..b3b9a9513c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## v1.9.1 + +- fix(cloud) misspelling +- fix(faas-adapter) fix the problem that the `include` behavior of the function code configuration `codeUri` does not meet expectations +- feat(cli) add templates related to file operations based on object storage +- feat(cli) delete unwanted attributes in the `malagu.yml` file output by the component merge + +- fix(cloud) 拼写错误 +- fix(faas-adapter) 修复函数代码配置 `codeUri` 的 `include` 行为不符合预期问题 +- feat(cli) 添加基于对象存储实现的文件操作相关的模板 +- feat(cli) 删除组件合并输出的 `malagu.yml` 文件中不需要的属性 + ## v1.9.0 - feat(cloud) abstracts a basic cloud computing component `@malagu/cloud`, abstracts unified interfaces and configurations for cloud products of different cloud vendors, developers can use unified interfaces to operate cloud service resources that do not require cloud vendors diff --git a/dev-packages/cli/src/init/templates.ts b/dev-packages/cli/src/init/templates.ts index 944a50f10a..f20bf7abe4 100644 --- a/dev-packages/cli/src/init/templates.ts +++ b/dev-packages/cli/src/init/templates.ts @@ -4,6 +4,7 @@ export const templates: any = { 'database-app': '{{ templatePath }}/database-app', 'admin-app': '{{ templatePath }}/admin-app', 'microservice': '{{ templatePath }}/microservice', + 'file-service': '{{ templatePath }}/file-service', 'puppeteer': '{{ templatePath }}/puppeteer', 'multi-component': '{{ templatePath }}/multi-component', 'mycli': '{{ templatePath }}/mycli', diff --git a/dev-packages/cli/src/package/application-config.ts b/dev-packages/cli/src/package/application-config.ts index 25a5b669e1..89a299aea8 100644 --- a/dev-packages/cli/src/package/application-config.ts +++ b/dev-packages/cli/src/package/application-config.ts @@ -63,6 +63,17 @@ export class ApplicationConfig { delete config.frontend; config = mergeWith(config, this.props[target], customizer); + delete config.webpackHooks; + delete config.initHooks; + delete config.configHooks; + delete config.buildHooks; + delete config.deployHooks; + delete config.serveHooks; + delete config.cliHooks; + delete config.modules; + delete config.staticModules; + delete config.assets; + config.targets = this.options.targets.length ? this.options.targets : (config.targets || [ FRONTEND_TARGET, BACKEND_TARGET ]); config.targets = Array.from(new Set(config.targets)); self[configProperty] = config; diff --git a/dev-packages/cli/templates/file-service/.github/workflows/deploy.yml b/dev-packages/cli/templates/file-service/.github/workflows/deploy.yml new file mode 100644 index 0000000000..3ab5e6a39e --- /dev/null +++ b/dev-packages/cli/templates/file-service/.github/workflows/deploy.yml @@ -0,0 +1,25 @@ +name: Malagu Deploy + +on: push + +jobs: + malagu-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v1 + with: + node-version: '12' + - uses: bahmutov/npm-install@v1 + - run: npm run lint --if-present + - run: npm test + - if: ${{ github.ref == 'refs/heads/master' }} + env: ${{ secrets }} + run: npx malagu deploy -m prod + - if: ${{ github.ref == 'refs/heads/pre' }} + env: ${{ secrets }} + run: npx malagu deploy -m pre + - if: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/pre' }} + env: ${{ secrets }} + run: npx malagu deploy -m test diff --git a/dev-packages/cli/templates/file-service/.gitignore b/dev-packages/cli/templates/file-service/.gitignore new file mode 100644 index 0000000000..f283ecc3fa --- /dev/null +++ b/dev-packages/cli/templates/file-service/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +dist +lib +.malagu +.env +*.log +.idea +.metadata +*.iml +jdt.ls-java-project +lerna-debug.log +.nyc_output +coverage +errorShots +.browser_modules +**/docs/api +package-backup.json +.history +.Trash-* +packages/plugin/typedoc +plugins +.local-chromium diff --git a/dev-packages/cli/templates/file-service/.vscode/launch.json b/dev-packages/cli/templates/file-service/.vscode/launch.json new file mode 100644 index 0000000000..f179008fa3 --- /dev/null +++ b/dev-packages/cli/templates/file-service/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "malagu serve", + "program": "${workspaceRoot}/node_modules/@malagu/cli/bin/malagu", + "args": [ "serve" ], + "cwd": "${workspaceFolder}", + "sourceMaps": true, + "env": { + "NODE_ENV": "development" + }, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + } + ] +} \ No newline at end of file diff --git a/dev-packages/cli/templates/file-service/README.md b/dev-packages/cli/templates/file-service/README.md new file mode 100644 index 0000000000..8322035d76 --- /dev/null +++ b/dev-packages/cli/templates/file-service/README.md @@ -0,0 +1,93 @@ +# 开发说明 + +## 安装依赖 + +以下使用 yarn 工具来说明,你也可以使用 npm。 + +```bash +# 通过 malagu init 初始化应用的时候已经自动安装了依赖,所以你只需要安装你额外需要的依赖即可 + +$ yarn add xxxx +``` + +## 本地运行 + +```shell +# 启动本地服务,端口默认 3000 +# 在终端中会输出本地服务的 URL 链接 + +$ yarn start # 或者执行 malagu serve 命令 +``` + +## 构建部署 + +模板默认提供了四套隔离环境:本地(local)、测试(test)、预发(pre)、线上(prod)。每个环境对于这一个 malagu 配置文件(可选),类似 malagu-test.yml。而 malagu.yml 文件一般用于放所有环境的公共配置。第一次部署的时候可能会提示你填写相关云厂商 ak 信息。如果是 Vercel 云平台的模板,会提示你需要登录到 Vercel 平台。你也可以在项目通过 .env 提供云厂商的 ak 信息。 + +```bash + +$ yarn deploy # 部署到测试环境 +$ yarn deploy:test # 部署到测试环境 +$ yarn deploy:pre # 部署到预发环境 +$ yarn deploy:prod # 部署到线上环境 + +``` + +## 关于 Malagu Framework + +Malagu 是基于 TypeScript 的 Serverless First、组件化、平台无关的渐进式应用框架。 + + +### 特征 + +- 约定大于配置,零配置,开箱即用 +- TypeScript 版 Spring Boot +- Serverless First +- 平台不锁定 +- 支持前后端一体化,前端框架不锁定 +- 支持微服务 +- 组件化,渐进式 +- 命令行工具插件化 +- 依赖注入 +- 面向切面编程(AOP) +- 集成了流行的 ORM 框架,使用装饰器声明式事务管理 +- 支持 OIDC 认证 +- 支持 OAuth2 授权 +- 使用 rxjs 管理状态 +- 提供 REST 和 RPC 两种接口风格 + +Malagu 名字由来:在我的家乡,谐音“吗啦咕”是小石头的意思,小石头堆砌起来可以建成高楼大厦、道路桥梁,而 Malagu 组件编排可以实现千变万化的应用。 + +### 快速开始 + +```bash +# 安装命令行工具 +npm install -g yarn +npm install -g @malagu/cli + +# 初始化 +malagu init project-name +cd project-name # 进入项目根目录 + +# 运行 +malagu serve + +# 部署 +malagu deploy +``` + +### 文档 + +- [介绍](https://www.yuque.com/cellbang/malagu/puw7p0) +- [快速开始](https://www.yuque.com/cellbang/malagu/qmq79k) +- [命令行工具](https://www.yuque.com/cellbang/malagu/xbfpir) +- [控制器](https://www.yuque.com/cellbang/malagu/cbgl7g) +- [数据库操作](https://www.yuque.com/cellbang/malagu/ztbcwq) +- [微服务](https://www.yuque.com/cellbang/malagu/wtiy6s) +- [认证与授权](https://www.yuque.com/cellbang/malagu/qhl0km) +- [云平台适配](https://www.yuque.com/cellbang/malagu/hh1mng) +- [依赖注入](https://www.yuque.com/cellbang/malagu/fw025h) +- [组件设计](https://www.yuque.com/cellbang/malagu/qaqomw) +- [前端架构](https://www.yuque.com/cellbang/malagu/vl9wbw) +- [React 开发](https://www.yuque.com/cellbang/malagu/fum7u8) +- [前后端一体化开发](https://www.yuque.com/cellbang/malagu/fi6lxi) + diff --git a/dev-packages/cli/templates/file-service/malagu.yml b/dev-packages/cli/templates/file-service/malagu.yml new file mode 100644 index 0000000000..7c0b6c9200 --- /dev/null +++ b/dev-packages/cli/templates/file-service/malagu.yml @@ -0,0 +1,14 @@ +targets: + - backend +# malagu: + # cloud: + # client: + # 本地测试需要打开,通过公网访问对象存储 + # internal: false + # 如果不填写 region 和 credentials,自动使用函数所在的 region,以及服务角色自动生成的临时 AK,该服务角色需要拥有访问以下 bucket 的读权限 + # region: cn-hangzhou + # credentials: + # accessKeyId: xxxxxxx + # accessKeySecret: xxxxxx +app: + bucket: malagu-bucket # 指定你自己的 bucket diff --git a/dev-packages/cli/templates/file-service/package.json b/dev-packages/cli/templates/file-service/package.json new file mode 100644 index 0000000000..078029b116 --- /dev/null +++ b/dev-packages/cli/templates/file-service/package.json @@ -0,0 +1,31 @@ +{ + "name": "file-service", + "keywords": [ + "malagu-component" + ], + "version": "0.0.0", + "license": "MIT", + "files": [ + "lib", + "src" + ], + "dependencies": { + "@malagu/fc-adapter": "latest", + "@malagu/mvc": "latest", + "@malagu/oss": "latest" + }, + "devDependencies": { + "@malagu/cli": "latest", + "@types/mime-types": "^2.1.0", + "rimraf": "^2.6.3" + }, + "scripts": { + "clean": "rimraf lib dist .malagu", + "build": "malagu build", + "start": "malagu serve", + "deploy": "malagu deploy -m test", + "deploy:test": "malagu deploy -m test", + "deploy:pre": "malagu deploy -m pre", + "deploy:prod": "malagu deploy -m prod" + } +} diff --git a/dev-packages/cli/templates/file-service/src/file-controller.ts b/dev-packages/cli/templates/file-service/src/file-controller.ts new file mode 100644 index 0000000000..f11e92ed3b --- /dev/null +++ b/dev-packages/cli/templates/file-service/src/file-controller.ts @@ -0,0 +1,47 @@ +import { Autowired, Value } from '@malagu/core'; +import { HttpError, Context } from '@malagu/web/lib/node'; +import { HttpHeaders, HttpStatus } from '@malagu/web'; +import { ObjectStorageService, RawCloudService } from '@malagu/cloud'; +import { Controller, Get, Query } from '@malagu/mvc/lib/node'; +import { contentType } from 'mime-types'; + +@Controller('files') +export class FileController { + + @Autowired(ObjectStorageService) + protected readonly objectStorageService: ObjectStorageService; + + @Value('app.bucket') + protected readonly bucket: string; + + @Get() + async merge(@Query('key') key: string) { + return this.doMerge(key); + } + + private async doMerge(key: string) { + if (!key) { + throw new HttpError(HttpStatus.BAD_REQUEST, 'Missing request parameter "key".'); + } + const keys = key.split(','); + + const res = Context.getResponse(); + res.set(HttpHeaders.CONTENT_TYPE, contentType(key) || ''); + res.set(HttpHeaders.CACHE_CONTROL, 'max-age=360000'); + + for (const k of keys) { + const stream = await this.objectStorageService.getStream({ bucket: this.bucket, key: k }); + await new Promise((resolve, reject) => { + stream.on('error', (err) => { + reject(err); + }); + + stream.on('end', () => { + resolve(); + }); + stream.pipe(res, { end: false }); + }); + } + + } +} \ No newline at end of file diff --git a/dev-packages/cli/templates/file-service/src/home-controller.ts b/dev-packages/cli/templates/file-service/src/home-controller.ts new file mode 100644 index 0000000000..79828c8f36 --- /dev/null +++ b/dev-packages/cli/templates/file-service/src/home-controller.ts @@ -0,0 +1,11 @@ +import { Controller, Get, Text } from '@malagu/mvc/lib/node'; + +@Controller() +export class HomeController { + + @Get() + @Text() + home(): string { + return 'Welcome to Malagu'; + } +} diff --git a/dev-packages/cli/templates/file-service/src/module.ts b/dev-packages/cli/templates/file-service/src/module.ts new file mode 100644 index 0000000000..4c4be8afa3 --- /dev/null +++ b/dev-packages/cli/templates/file-service/src/module.ts @@ -0,0 +1,5 @@ +import './home-controller'; +import './file-controller'; +import { autoBind } from '@malagu/core'; + +export default autoBind(); diff --git a/dev-packages/cli/templates/file-service/tsconfig.json b/dev-packages/cli/templates/file-service/tsconfig.json new file mode 100644 index 0000000000..a432a927b7 --- /dev/null +++ b/dev-packages/cli/templates/file-service/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "noImplicitAny": true, + "noEmitOnError": false, + "noImplicitThis": true, + "noUnusedLocals": true, + "strictNullChecks": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "downlevelIteration": true, + "module": "commonjs", + "moduleResolution": "node", + "jsx": "react", + "target": "es5", + "lib": [ + "es6", + "dom" + ], + "sourceMap": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/packages/cloud/src/common/cloud-protocol.ts b/packages/cloud/src/common/cloud-protocol.ts index 39b6cf991e..9e751f1fcc 100644 --- a/packages/cloud/src/common/cloud-protocol.ts +++ b/packages/cloud/src/common/cloud-protocol.ts @@ -43,7 +43,7 @@ export abstract class AbstractCloudService implements CloudService { const clientOptions = ConfigUtil.get(clientProp) || await this.clientOptionsProvider.provide() || { internal: true }; const region = ConfigUtil.get(regionProp) || await this.regionProvider.provide(); if (!region) { - throw Error(`Please configure region through the properties "malagu.cloud.credentials" or "${regionProp}"`); + throw Error(`Please configure region through the properties "malagu.cloud.region" or "${regionProp}"`); } const credentials = ConfigUtil.get(credentialsProp) || await this.credentialsProvider.provide(); if (!credentials) { diff --git a/packages/faas-adapter/src/hooks/code-loader.ts b/packages/faas-adapter/src/hooks/code-loader.ts index ad8da4c14f..34d06948f7 100644 --- a/packages/faas-adapter/src/hooks/code-loader.ts +++ b/packages/faas-adapter/src/hooks/code-loader.ts @@ -30,18 +30,12 @@ export class DefaultCodeLoader implements CodeLoader { const files = readdirSync(codeDir); for (const fileName of files) { const fullPath = join(codeDir, fileName); - if (codeUri?.include) { - const includes = typeof codeUri.include === 'string' ? new RegExp(codeUri.include) : codeUri.include; - if (!includes.test(fullPath)) { - return; - } - } - if (codeUri?.exclude) { - const exclude = typeof codeUri.exclude === 'string' ? new RegExp(codeUri.exclude) : codeUri.exclude; - if (exclude.test(fullPath)) { + if (!codeUri?.include || !this.match(codeUri.include, fullPath)) { + if (codeUri?.exclude && this.match(codeUri.exclude, fullPath)) { return; } } + const file = statSync(fullPath); if (file.isDirectory()) { const dir = zip.folder(fileName); @@ -54,4 +48,9 @@ export class DefaultCodeLoader implements CodeLoader { } } + protected match(pattern: string | RegExp, fullPath: string) { + const regx = typeof pattern === 'string' ? new RegExp(pattern) : pattern; + return regx.test(fullPath); + } + }