diff --git a/.eslintrc.js b/.eslintrc.js index ef8f8f961f83867cfe3e41cbd2d4cb745beb0aed..e1ad78ebc517437213c6b17f8035f31ee12d36cd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,5 @@ module.exports = { - extends: [ - '@cloud/eslint-config-cbc/typescript' - ], + parserOptions: { // recommend to use another config file like tsconfig.eslint.json and extends tsconfig.json in it. // because you may be need to lint test/**/*.test.ts but no need to emit to js. diff --git a/README.zh-CN.md b/README.zh-CN.md index 47f1ea9a15fda025931b1ba9f0fe93944836335a..081a86c8b7d6386381298f50cb24eb3c88e382f1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,7 +4,7 @@

-

TinyEngine低代码引擎使能开发者定制低代码平台,支持在线实时构建低码平台,支持二次开发或集成低码平台能力

+

TinyEngine低代码引擎能使开发者定制低代码平台,支持在线实时构建低码平台,支持二次开发或集成低码平台能力

[English](README.md) | 简体中文 diff --git a/app/controller/app-center/apps.ts b/app/controller/app-center/apps.ts index c7fd630814472e8b56aa367373b5bc0829f962ee..b038ab9ed253dd4e1c41cd29651dd21be56bfc13 100644 --- a/app/controller/app-center/apps.ts +++ b/app/controller/app-center/apps.ts @@ -12,7 +12,7 @@ import { Controller } from 'egg'; import { I_Response } from '../../lib/interface'; import { E_CanvasEditorState, E_ErrorCode, } from '../../lib/enum'; -import { publishAppRule } from '../../validate/app-center/app'; +import { publishAppRule, updateAppRule } from '../../validate/app-center/app'; const i18nRule = { id: { @@ -37,6 +37,31 @@ class AppsController extends Controller { this.ctx.body = await apps.getAppById(id); } + /** + * @router get /api/apps/update/:id 路径 + * @summary 应用修改 + * @description 修改应用 + */ + async update() { + const { id } = this.ctx.params; + const { body } = this.ctx.request; + const updateParam = this.ctx.helper.pickObject({ id, ...body }, [ + 'platform', + 'obs_url', + 'created_at', + 'updated_at', + 'createdBy', + 'updatedBy', + 'tenant', + 'platform_history', + 'framework', + 'data_hash' + ], false) + const { apps } = this.ctx.service.appCenter; + this.ctx.validate(updateAppRule, updateParam); + this.ctx.body = await apps.updateApp(updateParam); + } + /** * @router get /api/apps/i18n/:id 路径 diff --git a/app/controller/material-center/block.ts b/app/controller/material-center/block.ts index 21355cedf9a7da01d4cff3c6458e97fd4b7b89ef..c2e2428c1dbf9e7ac7c66c610b4d9c86cb023d67 100644 --- a/app/controller/material-center/block.ts +++ b/app/controller/material-center/block.ts @@ -124,10 +124,10 @@ export default class BlockController extends Controller { createParam.tags = createParam.tags.filter((tag) => !!tag); } // 处理区块截图 - if (createParam.screenshot) { - const url = await this.service.materialCenter.block.handleScreenshot(createParam); - createParam.screenshot = url; - } + // if (createParam.screenshot) { + // const url = await this.service.materialCenter.block.handleScreenshot(createParam); + // createParam.screenshot = url; + // } this.ctx.body = await this.service.materialCenter.block.create(createParam); } @@ -155,8 +155,8 @@ export default class BlockController extends Controller { } // 处理区块截图 if (updateParam.screenshot && updateParam.label) { - const url = await this.service.materialCenter.block.handleScreenshot(updateParam); - updateParam.screenshot = url; + //const url = await this.service.materialCenter.block.handleScreenshot(updateParam); + updateParam.screenshot = ''; } else { // 不更新 screenshot, 避免screenshot为空字符串覆盖上次值 delete updateParam.screenshot; @@ -166,14 +166,16 @@ export default class BlockController extends Controller { async delete() { const { id } = this.ctx.params; - const { obs } = this.service; + //const { obs } = this.service; const res = await this.service.materialCenter.block.delete({ id }); // 批量删除obs资源 + /* const { label } = res.data; const keyObjs: Array = await obs.list(label, 'block'); if (keyObjs.length > 0) { await obs.delete({ Objects: keyObjs }); } + */ this.ctx.body = res; } async build() { @@ -196,7 +198,8 @@ export default class BlockController extends Controller { throwApiError('', Number(E_ErrorCode.BadRequest), E_MaterialErrorCode.CM205); } // 处理区块截图 - block.screenshot = await materialCenter.block.handleScreenshot(block); + // block.screenshot = await materialCenter.block.handleScreenshot(block); + block.screenshot = ''; // 更新i18n 信息 block.i18n = await materialCenter.block.getBlockI18n(id); // 如果有未完成的任务直接返回该任务信息 diff --git a/app/extend/helper.ts b/app/extend/helper.ts index dc52e032d2dbd7347722fbd097961ec0d94104f0..7bca1ee97f1d6e74cbc5ef27ef8f4b77c587714b 100644 --- a/app/extend/helper.ts +++ b/app/extend/helper.ts @@ -10,6 +10,7 @@ * */ import { Context } from 'egg'; +import spawn from "cross-spawn" import { I_ErrorData, I_Response, I_DataUnnecessary } from '../lib/interface'; import { I_UpdateAppParam } from '../interface/app-center/app'; import { E_ErrorCode, E_MaterialErrorCode } from '../lib/enum'; @@ -231,5 +232,44 @@ module.exports = { } }); return result; - } + }, + + async execCommand (command,options) { + const [cmd, ...params] = command.split(' ') + return new Promise((resolve, reject) => { + const task = spawn(cmd, params, { ...options }) + let stderr = '' + task.on('close', (code) => { + if (code === 0) { + return resolve(`succeed exec command: ${command}`) + } + return reject(new Error(stderr.trim())) + }) + task.on('error', reject) + task.stderr?.on('data', (chunk) => { + stderr += chunk.toString() + }) + task.stdout?.pipe(process.stdout) + task.stderr?.pipe(process.stderr) + }) + }, + + async execCommandWithCatch (commands,options,messagePrefix) { + try { + for(const command of commands ){ + await this.ctx.helper.execCommand(command, options) + } + return { + isSuccess: true, + message: `${messagePrefix}: success` + } + } catch (error: any) { + const message = `${messagePrefix} error: ${error.message || error}` + return { + isSuccess: false, + message: message + } + } + }, + }; diff --git a/app/router/appCenter/base.ts b/app/router/appCenter/base.ts index 39e99a6410c55afcd6fd6ab5e96c393042796a19..c406442d1c30dae8773bd6c3caeeb68a0b65c056 100644 --- a/app/router/appCenter/base.ts +++ b/app/router/appCenter/base.ts @@ -24,6 +24,7 @@ export default (app: Application) => { // 应用管理 subRouter.get('/apps/detail/:id', controller.appCenter.apps.detail); + subRouter.post('/apps/update/:id', controller.appCenter.apps.update); // 关联应用信息,主要是apps下block-histories subRouter.get('/apps/associate', controller.appCenter.apps.associate); diff --git a/app/service/app-center/publish.ts b/app/service/app-center/publish.ts index 135f86fa8207645e702444815488161c20f38b07..9cb8099ab32902fec179fbf9e8708dc459fcb5a7 100644 --- a/app/service/app-center/publish.ts +++ b/app/service/app-center/publish.ts @@ -46,9 +46,8 @@ class PublishApp extends DataServcice { try { await generate.init(appId); - - const appInfo = await this.service.appCenter.apps.getAppById(Number(appId)); - const { project_name, branch } = appInfo.data || {}; + const project_name = this.config.projectName; + const branch = this.config.gitBranch; const git_branch = this.bodyParam.branch || branch; const canCreateNewBranch = this.bodyParam.canCreateNewBranch || false; diff --git a/app/service/cnpm.ts b/app/service/cnpm.ts index fec05621c050138133226e806f40c613b38e1d25..1cef34dae817dbcb05c63b295afe7c84ee2f226a 100644 --- a/app/service/cnpm.ts +++ b/app/service/cnpm.ts @@ -19,7 +19,6 @@ export default class CnpmService extends Service { async loginInNpm(packagePath) { const commands = [ 'npm config set strict-ssl false', - 'npm config set always-auth true', `npm config set registry https://${this.registry}`, `npm config set //${this.registry}:_authToken=${this.authToken}`, `npm whoami --registry https://${this.registry}` @@ -31,5 +30,5 @@ export default class CnpmService extends Service { const commands = ['npm publish --access=public']; return this.ctx.helper.execCommandWithCatch(commands, { cwd: packagePath }, 'publish cnpm'); } - + } diff --git a/app/service/material-center/Block.ts b/app/service/material-center/Block.ts index a67dfcdd76a97acbe0d60a71e8b3ad773ac2f84e..6fa65951d86425303e4ce682d0ae03e3b10bce05 100644 --- a/app/service/material-center/Block.ts +++ b/app/service/material-center/Block.ts @@ -12,9 +12,8 @@ import * as fs from 'fs-extra'; import * as qs from 'querystring'; import DataService from '../dataService'; -import { E_ErrorCode, E_i18Belongs, E_MaterialErrorCode, E_Method } from '../../lib/enum'; +import { E_i18Belongs, E_Method } from '../../lib/enum'; import { I_Response } from '../../lib/interface'; -import { ApiError } from '../../lib/ApiError'; import { I_CreateBlock, I_UpdateBlock } from '../../interface/material-center/block'; export default class BlockService extends DataService { @@ -54,17 +53,10 @@ export default class BlockService extends DataService { }); } async delete({ id }) { - const materials: I_Response = await this.query({ - url: `${this.base}/${id}/materials` + return this.query({ + url: `${this.base}/${id}`, + method: E_Method.Delete }); - if (materials.data?.count > 0) { - throw new ApiError('', E_ErrorCode.BadRequest, E_MaterialErrorCode.CM203); - } else { - return this.query({ - url: `${this.base}/${id}`, - method: E_Method.Delete - }); - } } getBlocks(param) { diff --git a/app/service/material-center/blockBuilder.ts b/app/service/material-center/blockBuilder.ts index fc99071336f750c285aacdd574190aec83f0ca57..1e93e458573df95606f824d5ed4a7e6498d2760d 100644 --- a/app/service/material-center/blockBuilder.ts +++ b/app/service/material-center/blockBuilder.ts @@ -14,7 +14,6 @@ import fs from 'fs-extra' import { Service } from 'egg'; import { E_TASK_STATUS } from '../../lib/enum' import {glob} from 'glob' - export default class BlockBuilder extends Service{ unpkgBaseUrl = 'https://npm.onmicrosoft.cn' pkgName @@ -22,10 +21,11 @@ export default class BlockBuilder extends Service{ // 将服务分为两种,一种是BuildService , 一种是操作数据库的DataService async start(blockId, taskId, body) { + const {task} = this.ctx.service this.ctx.logger.info('开始区块构建', blockId, taskId) // 查询区块数据 const { data: block } = await this.service.materialCenter.block.findById(blockId) - await this.service.task.update({ + await task.update({ id: taskId, progress: 'generating code', taskStatus: E_TASK_STATUS.RUNNING @@ -38,18 +38,12 @@ export default class BlockBuilder extends Service{ needToSave } = body - await this.service.task.update({ - id: taskId, - progress: 'generating code', - taskStatus: E_TASK_STATUS.RUNNING - }) - const modifiedBlock = { ...block, ...others, id: blockId, version: blockVersion } try { // 调用DSL转换方法生成代码 const sourceCode = await this.translate(modifiedBlock, { content }) - await this.service.task.update({ + await task.update({ id: taskId, progress: 'generating code completed', progress_percent: 10 @@ -63,7 +57,7 @@ export default class BlockBuilder extends Service{ const buildResult = { result: true, versions, endTime: new Date().toLocaleString() } const buildInfo = { - buildResult, + ...buildResult, message, filesPath, taskId @@ -123,9 +117,10 @@ export default class BlockBuilder extends Service{ if (needToSave) { blockData.content = content } - - await this.service.BlockService.update(blockData) - await this.service.task.update({ + + await this.service.materialCenter.block.update(blockData) + const {task} = this.ctx.service + await task.update({ id: taskId, progress_percent: 100, taskResult: JSON.stringify({ result: 'block building completed' }), @@ -135,6 +130,7 @@ export default class BlockBuilder extends Service{ // 错误处理统一方法 async afterBuildFailed(blockId, taskId, error) { + const {task} = this.ctx.service this.ctx.logger.error(`build block ${blockId} error:`, error) const buildInfo = { result: false, message: error.message, endTime: new Date().toLocaleString() } @@ -142,7 +138,7 @@ export default class BlockBuilder extends Service{ id: blockId, last_build_info: buildInfo }) - await this.service.task.update({ + await task.update({ id: taskId, taskStatus: E_TASK_STATUS.STOPPED, taskResult: JSON.stringify({ result: error.message }) @@ -150,24 +146,22 @@ export default class BlockBuilder extends Service{ } async buildWebComponent(sourceCode, block, taskId, version) { - const { framework = 'Vue', label } = block - const BuildService = { - Vue: this.service.materialCenter.vueBlockBuilder - } + const { label } = block // 初始化构建目录 - const service = new BuildService[framework]() + const service = this.service.materialCenter.vueBlockBuilder + const {task} = this.ctx.service try { - await this.service.task.update({ id: taskId, progress: 'installing deps' }) + await task.update({ id: taskId, progress: 'installing deps' }) await service.init() - await this.service.task.update({ id: taskId, progress: 'deps installed', progress_percent: 40 }) - const config = await service.readConfig() + await task.update({ id: taskId, progress: 'deps installed', progress_percent: 40 }) + const config: any = await service.readConfig() // 写入口文件 if (service.writeEntryFile) { await service.writeEntryFile(block, config.path, version) } // 注入代码 await service.injectCodeFile(sourceCode, config.path) - await this.service.task.update({ id: taskId, progress: 'code injected', progress_percent: 50 }) + await task.update({ id: taskId, progress: 'code injected', progress_percent: 50 }) // 写配置 const className = this.kebabToPascalCase(label) config.data = { @@ -179,14 +173,14 @@ export default class BlockBuilder extends Service{ componentPath: sourceCode.find((c) => c.type === 'Block')?.filePath ?? '' // 指定block路径 } } - await this.service.task.update({ id: taskId, progress: 'write config', progress_percent: 55 }) + await task.update({ id: taskId, progress: 'write config', progress_percent: 55 }) await service.writeConfig(JSON.stringify(config, null, 2)) - await this.service.task.update({ id: taskId, progress: 'building', progress_percent: 60 }) + await task.update({ id: taskId, progress: 'building', progress_percent: 60 }) // 清理dist目录 service.clearDist() // 构建 await service.build() - await this.service.task.update({ id: taskId, progress: 'building completed', progress_percent: 80 }) + await task.update({ id: taskId, progress: 'building completed', progress_percent: 80 }) // 返回静态资源输出路径 const distPath = service.getDist() // 转换config.umd.min.js配置文件为 bundle.json @@ -194,7 +188,7 @@ export default class BlockBuilder extends Service{ // 发布区块到 npm const filesPath = await this.publish(distPath, block, version) // 获取构建原料版本 - const versions = service.getBuildInfo([BuildService[framework].baseNpm]) + const versions = service.getBuildInfo([service.baseNpm]) return { filesPath, versions } } finally { await service.clear() @@ -237,7 +231,7 @@ export default class BlockBuilder extends Service{ blocksData = innerBlocks.data.map(({ content, label }) => ({ content, label })).filter((b) => b.label !== label) } - const { generateCode } = require('@opentiny/lowcode-dsl-vue') + const { generateCode } = require('@opentiny/tiny-engine-dsl-vue') const result = generateCode({ pageInfo: { schema: content, name: label }, blocksData }) return result } @@ -294,11 +288,12 @@ export default class BlockBuilder extends Service{ async publishPackage(folder, blockInfo, version) { const pkgJson = this.generatePackageJson(blockInfo, version) await fs.writeJson(path.resolve(folder, './package.json'), pkgJson) - const loginInRes = await this.service.cnpmService.loginInNpm(folder) + const { cnpm } = this.ctx.service; + const loginInRes = await cnpm.loginInNpm(folder) if (!loginInRes.isSuccess) { return loginInRes } - return this.service.cnpmService.publishCnpm(folder) + return cnpm.publishCnpm(folder) } // 生成npm 包的package.json diff --git a/app/service/material-center/vueBlockBuilder.ts b/app/service/material-center/vueBlockBuilder.ts index aaccea3e09f81797ace87b6f95c27e897f5c0331..c64d8f1cc41da0a7588fedcc4f9bc494ae97a6c4 100644 --- a/app/service/material-center/vueBlockBuilder.ts +++ b/app/service/material-center/vueBlockBuilder.ts @@ -123,7 +123,7 @@ export default class VueBlockBuilder extends Service{ // 获取tgz路径 async findTgz(dir) { const fileList = await fs.readdir(dir) - return fileList.find((file) => /^opentiny-tiny-engine-block-build-*\.tgz$/.test(file)) || '' //TODO 这里匹配的是包tgz文件的名称,择机替换为opentiny的 + return fileList.find((file) => /^opentiny-tiny-engine-block-build-0.*\.tgz$/.test(file)) || '' //TODO 这里匹配的是包tgz文件的名称,择机替换为opentiny的 } // 执行命令 diff --git a/app/tiny-engine-transform/.eslintrc.js b/app/tiny-engine-transform/.eslintrc.js index 39ad05e4f86af4d4b1487539b0f33c4a7d3b117f..7fee4f0e3db7711bdb5e59e536b4acd684299e10 100644 --- a/app/tiny-engine-transform/.eslintrc.js +++ b/app/tiny-engine-transform/.eslintrc.js @@ -1,5 +1,4 @@ module.exports = { - extends: ['@cloud/eslint-config-cbc/typescript'], parserOptions: { // recommend to use another config file like tsconfig.eslint.json and extends tsconfig.json in it. // because you may be need to lint test/**/*.test.ts but no need to emit to js. diff --git a/app/tiny-engine-transform/package.json b/app/tiny-engine-transform/package.json index c52ed18235d2e84a4e2cdb4dbd9088bb21985e1b..382098752d6e6d54f997a126fb8f84107614c161 100644 --- a/app/tiny-engine-transform/package.json +++ b/app/tiny-engine-transform/package.json @@ -17,7 +17,6 @@ "author": "", "license": "ISC", "devDependencies": { - "@cloud/eslint-config-cbc": "^1.7.9", "@types/node": "^12.20.16", "@typescript-eslint/eslint-plugin": "^5.35.1", "@typescript-eslint/parser": "^5.35.1", diff --git a/config/config.default.ts b/config/config.default.ts index b18bb29632fcc0a36f59be8f030a9cac5c601f09..942614acf9da0abaeb45529c5716d2fff041a753 100644 --- a/config/config.default.ts +++ b/config/config.default.ts @@ -277,7 +277,7 @@ export default (appInfo) => { }; }; - config.cnpmRegistryOptions = [ + config.npmRegistryOptions = [ '--registry=https://registry.npmjs.org/', ]; @@ -285,6 +285,8 @@ export default (appInfo) => { config.baseNpm = '@opentiny/tiny-engine-block-build'; config.authToken = process.env.NPM_AUTH_TOKEN; // 替换为自己的npm token config.registry = 'registry.npmjs.org/'; + config.projectName = process.env.GIT_REPO; // 应用发布git仓库地址 + config.gitBranch = process.env.GIT_BRANCH; // 应用发布git代码默认提交分支 return config; }; diff --git a/package.json b/package.json index bc644e9dabb53eba3d2baed8b80f89eea27775ba..dc99f266d27f79fc6907e9f2d3bf18ac125cc3be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,13 @@ { "name": "tiny-engine-webservice", "version": "1.0.0", - "description": "", + "description": "tiny-engine-webservice is a restful API responsible for providing data services, code generation services, and code release services to the front end. It does not directly operate on the database, and data operations request interfaces from tiny-engine-data-center", + "homepage": "https://opentiny.design/tiny-engine", + "keywords": [ + "node.js", + "tiny-engine-webservice", + "lowcode" + ], "private": true, "egg": { "typescript": true, @@ -20,6 +26,16 @@ "lint": "eslint . --ext .ts --fix", "clean": "ets clean" }, + "repository": { + "type": "git", + "url": "https://github.com/opentiny/tiny-engine-webservice", + "directory": "" + }, + "bugs": { + "url": "https://github.com/opentiny/tiny-engine-webservice/issues" + }, + "author": "OpenTiny Team", + "license": "MIT", "nyc": { "check-coverage": false, "lines": 60, @@ -29,8 +45,8 @@ }, "dependencies": { "@opentiny/tiny-engine-block-build": "^0.0.1-alpha.0", - "@opentiny/tiny-engine-dsl-vue": "^1.0.3-alpha.0", - "@opentiny/tiny-engine-transform": "^1.0.0", + "@opentiny/tiny-engine-dsl-vue": "~1.0.1", + "@opentiny/tiny-engine-transform": "^1.0.0-alpha.1", "compressing": "^1.7.0", "egg": "^3.17.5", "egg-amqplib": "^2.0.5", @@ -74,13 +90,7 @@ "engines": { "node": ">=8.9.0" }, - "repository": { - "type": "git", - "url": "" - }, "eslintIgnore": [ "coverage" - ], - "author": "", - "license": "MIT" + ] }