diff --git a/designer-demo/package.json b/designer-demo/package.json index 4e17ecd492..15bf982874 100644 --- a/designer-demo/package.json +++ b/designer-demo/package.json @@ -8,7 +8,8 @@ "build:alpha": "cross-env NODE_OPTIONS=--max-old-space-size=10240 vite build --mode alpha", "build": "cross-env NODE_OPTIONS=--max-old-space-size=10240 vite build", "test": "vitest run", - "test:watch": "vitest" + "test:watch": "vitest", + "uploadMaterials": "node ./scripts/uploadMaterials.mjs" }, "dependencies": { "@opentiny/tiny-engine": "workspace:^", @@ -28,6 +29,9 @@ "@opentiny/tiny-engine-vite-config": "workspace:^", "@vitejs/plugin-vue": "^5.1.2", "cross-env": "^7.0.3", + "dotenv": "^16.6.1", + "fs-extra": "^11.3.2", + "picocolors": "^1.1.1", "vite": "^5.4.2", "vitest": "3.0.9" } diff --git a/designer-demo/scripts/logger.mjs b/designer-demo/scripts/logger.mjs new file mode 100644 index 0000000000..5b1fe344eb --- /dev/null +++ b/designer-demo/scripts/logger.mjs @@ -0,0 +1,88 @@ +const log = (() => { + return (...args) => { + process.stdout.write(args.map(arg => + typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) + ).join(' ') + '\n'); + }; +})(); + +const warn = (() => { + return (...args) => { + process.stderr.write(args.map(arg => + typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) + ).join(' ') + '\n'); + }; +})(); + +class Logger { + constructor(command = 'default') { + this.command = command; + this.colors = null; + this.hasColors = false; + this.initColors(); + } + + async initColors() { + try { + const colorsModule = await import('colors'); + this.colors = colorsModule.default || colorsModule; + this.hasColors = true; + } catch (err) { + warn('colors package not found, using basic logging'); + this.hasColors = false; + } + } + + output(type, ...args) { + const time = new Date().toLocaleTimeString(); + const prefix = `[${this.command}] [${time}]`; + const message = args.map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2); + } + return String(arg); + }).join(' '); + + const outputFn = type === 'error' || type === 'warn' ? warn : log; + + if (this.hasColors && this.colors) { + const colorMap = { + info: this.colors.cyan, + warn: this.colors.yellow, + error: this.colors.red, + success: this.colors.green, + }; + const coloredType = colorMap[type] + ? colorMap[type](type.toUpperCase()) + : type.toUpperCase(); + + outputFn(`${prefix} ${coloredType} ${message}`); + } else { + const emojiMap = { + info: 'ℹ️', + warn: '⚠️', + error: '❌', + success: '✅', + }; + outputFn(`${prefix} ${emojiMap[type] || ''} ${message}`); + } + } + + success(...args) { + this.output('success', ...args); + } + + info(...args) { + this.output('info', ...args); + } + + warn(...args) { + this.output('warn', ...args); + } + + error(...args) { + this.output('error', ...args); + } +} + +export default Logger; diff --git a/designer-demo/scripts/uploadMaterials.mjs b/designer-demo/scripts/uploadMaterials.mjs new file mode 100644 index 0000000000..17aceaa876 --- /dev/null +++ b/designer-demo/scripts/uploadMaterials.mjs @@ -0,0 +1,72 @@ +import dotenv from 'dotenv' +import fs from 'fs-extra' +import { Buffer } from 'node:buffer' +import path from 'node:path' +import Logger from './logger.mjs' + + +/** + * 同步物料资产包到后端数据库 + * 1. 读取 env/.env.local 文件,获取后端地址。需要设置地址如:backend_url=http://localhost:9090 + * 2. 读取 public/mock/bundle.json 文件,获取物料资产包数据 + * 3. 将物料资产包数据通过 POST 请求上传到后端接口 /material-center/api/component/bundle/create + * 4. 检查数据库t_component表中数据是否更新成功 + * + * 使用场景: + * 1. 本地已经将 bundle.json 文件进行修改,但是数据需要同步到后端数据库中。 + * 2. 本地已经将 bundle.json 文件进行修改,但是出码仍然不正确。 + * @returns + */ +async function main() { + const logger = new Logger('uploadMaterials') + + // 先构造出.env*文件的绝对路径 + const appDirectory = fs.realpathSync(process.cwd()) + const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath) + const pathsDotenv = resolveApp('env') + logger.info(`Start to load .env.local file from ${pathsDotenv}/.env.local`) + dotenv.config({ path: `${pathsDotenv}/.env.local` }) + const { backend_url } = process.env + + if (!backend_url) { + logger.error('backend_url is not set in .env.local file') + process.exit(1) + } + + const bundlePath = path.join(process.cwd(), './public/mock/bundle.json') + logger.info(`Start to read bundle.json file from ${bundlePath}`) + const bundle = fs.readJSONSync(bundlePath) + const jsonBuffer = Buffer.from(JSON.stringify(bundle)) + + const requestUrl = (backend_url.endsWith('/') ? backend_url.slice(0, -1) : backend_url) + '/material-center/api/component/bundle/create' + logger.info(`Start to upload bundle.json file to ${requestUrl}`) + try { + const formData = new FormData() + formData.append('file', new Blob([jsonBuffer], { type: 'application/json'}), 'bundle.json') + const response = await fetch(requestUrl, { + method: 'POST', + body: formData + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Upload failed with status ${response.status}: ${errorText}`) + } + const data = await response.json() + if (data && data.success) { + logger.success(`File uploaded successfully:${JSON.stringify(data)}`) + } else { + logger.warn(`Upload completed but success flag is false: ${JSON.stringify(data)}`) + logger.warn(`Upload completed with warnings: ${JSON.stringify(data.message)}`) + } + } catch (error) { + logger.error('Error uploading file:', error instanceof Error ? error.message : String(error)) + } +} + +main() +.catch((e) => { + const logger = new Logger('uploadMaterials') + logger.error('Error uploading file:', e instanceof Error ? e.message : String(e)); + process.exit(1); +}) diff --git a/package.json b/package.json index 5e101abe25..38c87c8875 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "splitMaterials": "node ./scripts/splitMaterials.mjs", "buildMaterials": "node ./scripts/buildMaterials.mjs", "updateTemplate": "node ./scripts/updateTemplate.mjs", - "uploadMaterials": "node scripts/uploadMaterials.mjs" + "uploadMaterials": "pnpm --filter designer-demo uploadMaterials" }, "devDependencies": { "@eslint/js": "^8.57.1", diff --git a/packages/engine-cli/template/designer/package.json b/packages/engine-cli/template/designer/package.json index 895a70aa07..c920c270a3 100644 --- a/packages/engine-cli/template/designer/package.json +++ b/packages/engine-cli/template/designer/package.json @@ -7,6 +7,7 @@ "dev": "concurrently 'pnpm:serve:mock' 'pnpm:serve:frontend'", "build:alpha": "cross-env NODE_OPTIONS=--max-old-space-size=10240 vite build --mode alpha", "build": "cross-env NODE_OPTIONS=--max-old-space-size=10240 vite build", + "uploadMaterials": "node ./scripts/uploadMaterials.mjs", "serve:frontend": "cross-env vite", "serve:mock": "node node_modules/@opentiny/tiny-engine-mock/dist/app.js" }, @@ -28,6 +29,9 @@ "@opentiny/tiny-engine-vite-config": "^2.8.0", "@vitejs/plugin-vue": "^5.1.2", "cross-env": "^7.0.3", + "dotenv": "^16.6.1", + "fs-extra": "^11.3.2", + "picocolors": "^1.1.1", "vite": "^5.4.2", "concurrently": "^8.2.0" } diff --git a/packages/engine-cli/template/designer/scripts/logger.mjs b/packages/engine-cli/template/designer/scripts/logger.mjs new file mode 100644 index 0000000000..f19994e935 --- /dev/null +++ b/packages/engine-cli/template/designer/scripts/logger.mjs @@ -0,0 +1,88 @@ +const log = (() => { + return (...args) => { + process.stdout.write( + args.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg))).join(' ') + '\n' + ) + } +})() + +const warn = (() => { + return (...args) => { + process.stderr.write( + args.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg))).join(' ') + '\n' + ) + } +})() + +class Logger { + constructor(command = 'default') { + this.command = command + this.colors = null + this.hasColors = false + this.initColors() + } + + async initColors() { + try { + const colorsModule = await import('colors') + this.colors = colorsModule.default || colorsModule + this.hasColors = true + } catch (err) { + warn('colors package not found, using basic logging') + this.hasColors = false + } + } + + output(type, ...args) { + const time = new Date().toLocaleTimeString() + const prefix = `[${this.command}] [${time}]` + const message = args + .map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2) + } + return String(arg) + }) + .join(' ') + + const outputFn = type === 'error' || type === 'warn' ? warn : log + + if (this.hasColors && this.colors) { + const colorMap = { + info: this.colors.cyan, + warn: this.colors.yellow, + error: this.colors.red, + success: this.colors.green + } + const coloredType = colorMap[type] ? colorMap[type](type.toUpperCase()) : type.toUpperCase() + + outputFn(`${prefix} ${coloredType} ${message}`) + } else { + const emojiMap = { + info: 'ℹ️', + warn: '⚠️', + error: '❌', + success: '✅' + } + outputFn(`${prefix} ${emojiMap[type] || ''} ${message}`) + } + } + + success(...args) { + this.output('success', ...args) + } + + info(...args) { + this.output('info', ...args) + } + + warn(...args) { + this.output('warn', ...args) + } + + error(...args) { + this.output('error', ...args) + } +} + +export default Logger diff --git a/packages/engine-cli/template/designer/scripts/uploadMaterials.mjs b/packages/engine-cli/template/designer/scripts/uploadMaterials.mjs new file mode 100644 index 0000000000..763c91fb5f --- /dev/null +++ b/packages/engine-cli/template/designer/scripts/uploadMaterials.mjs @@ -0,0 +1,72 @@ +import dotenv from 'dotenv' +import fs from 'fs-extra' +import { Buffer } from 'node:buffer' +import path from 'node:path' +import Logger from './logger.mjs' + +/** + * 同步物料资产包到后端数据库 + * 1. 读取 env/.env.local 文件,获取后端地址。需要设置地址如:backend_url=http://localhost:9090 + * 2. 读取 public/mock/bundle.json 文件,获取物料资产包数据 + * 3. 将物料资产包数据通过 POST 请求上传到后端接口 /material-center/api/component/bundle/create + * 4. 检查数据库t_component表中数据是否更新成功 + * + * 使用场景: + * 1. 本地已经将 bundle.json 文件进行修改,但是数据需要同步到后端数据库中。 + * 2. 本地已经将 bundle.json 文件进行修改,但是出码仍然不正确。 + * @returns + */ +async function main() { + const logger = new Logger('uploadMaterials') + + // 先构造出.env*文件的绝对路径 + const appDirectory = fs.realpathSync(process.cwd()) + const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath) + const pathsDotenv = resolveApp('env') + logger.info(`Start to load .env.local file from ${pathsDotenv}/.env.local`) + dotenv.config({ path: `${pathsDotenv}/.env.local` }) + const { backend_url } = process.env + + if (!backend_url) { + logger.error('backend_url is not set in .env.local file') + process.exit(1) + } + + const bundlePath = path.join(process.cwd(), './public/mock/bundle.json') + logger.info(`Start to read bundle.json file from ${bundlePath}`) + const bundle = fs.readJSONSync(bundlePath) + const jsonBuffer = Buffer.from(JSON.stringify(bundle)) + + const requestUrl = + (backend_url.endsWith('/') ? backend_url.slice(0, -1) : backend_url) + + '/material-center/api/component/bundle/create' + logger.info(`Start to upload bundle.json file to ${requestUrl}`) + try { + const formData = new FormData() + formData.append('file', new Blob([jsonBuffer], { type: 'application/json' }), 'bundle.json') + const response = await fetch(requestUrl, { + method: 'POST', + body: formData + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Upload failed with status ${response.status}: ${errorText}`) + } + const data = await response.json() + if (data && data.success) { + logger.success(`File uploaded successfully:${JSON.stringify(data)}`) + } else { + logger.warn(`Upload completed but success flag is false: ${JSON.stringify(data)}`) + logger.warn(`Upload completed with warnings: ${JSON.stringify(data.message)}`) + } + } catch (error) { + logger.error('Error uploading file:', error instanceof Error ? error.message : String(error)) + } +} + +main().catch((e) => { + const logger = new Logger('uploadMaterials') + logger.error('Error uploading file:', e instanceof Error ? e.message : String(e)) + process.exit(1) +}) diff --git a/scripts/updateTemplate.mjs b/scripts/updateTemplate.mjs index 9557c5c626..b3edd01db1 100644 --- a/scripts/updateTemplate.mjs +++ b/scripts/updateTemplate.mjs @@ -2,7 +2,7 @@ import fs from 'fs-extra' import path from 'node:path' import { fileURLToPath } from 'node:url' import Logger from './logger.mjs' -import pkg from '../packages/design-core/package.json' assert { type: 'json' } +import pkg from '../packages/design-core/package.json' with { type: 'json' } const logger = new Logger('updateTemplate') diff --git a/scripts/uploadMaterials.mjs b/scripts/uploadMaterials.mjs deleted file mode 100644 index 9435f87e1d..0000000000 --- a/scripts/uploadMaterials.mjs +++ /dev/null @@ -1,40 +0,0 @@ -import { Buffer } from 'buffer' -import dotenv from 'dotenv' -import fs from 'fs-extra' -import path from 'node:path' -import Logger from './logger.mjs' - -const logger = new Logger('uploadMaterials') - -// 先构造出.env*文件的绝对路径 -const appDirectory = fs.realpathSync(process.cwd()) -const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath) -const pathsDotenv = resolveApp('.env') -dotenv.config({ path: `${pathsDotenv}.local` }) -const { backend_url } = process.env - -const bundlePath = path.join(process.cwd(), '/designer-demo/public/mock/bundle.json') -const bundle = fs.readJSONSync(bundlePath) -const jsonBuffer = Buffer.from(JSON.stringify(bundle)) -const boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW' -const formHeaders = { - 'Content-Type': `multipart/form-data; boundary=${boundary}`, -} - -let body = `--${boundary}\r\n` -body += 'Content-Disposition: form-data; name="file"; filename="bundle.json"\r\n' -body += 'Content-Type: application/json\r\n\r\n' -body += jsonBuffer.toString() + `\r\n--${boundary}--` - -fetch(backend_url, { - method: 'POST', - headers: formHeaders, - body: body, -}) - .then(response => response.json()) - .then(data => { - logger.success('File uploaded successfully:', data) - }) - .catch(error => { - logger.error('Error uploading file:', error) - })