tiny-engine/scripts/connection.mjs

438 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import mysql from 'mysql'
import Logger from './logger.mjs'
import fs from 'node:fs'
import path from 'node:path'
import dotenv from 'dotenv'
const logger = new Logger('buildMaterials')
// 先构造出.env*文件的绝对路径
const appDirectory = fs.realpathSync(process.cwd())
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath)
const pathsDotenv = resolveApp('.env')
// 加载.env.local
dotenv.config({ path: `${pathsDotenv}.local` })
const { SQL_HOST, SQL_PORT, SQL_USER, SQL_PASSWORD, SQL_DATABASE } = process.env
// 组件表名称
const componentsTableName = 'user_components'
// 组件关联到物料资产包的id
const materialHistoryId = 639
// 数据库配置
const mysqlConfig = {
host: SQL_HOST, // 主机名(服务器地址)
port: SQL_PORT, // 端口号
user: SQL_USER, // 用户名
password: SQL_PASSWORD, // 密码
database: SQL_DATABASE // 数据库名称
}
class MysqlConnection {
constructor(config) {
this.config = config || mysqlConfig
// 是否连接上了数据库
this.connected = false
this.connection = mysql.createConnection(this.config)
}
connect() {
return new Promise((resolve, reject) => {
this.connection.connect((error) => {
if (error) {
logger.warn('未能连接到数据库,请查看数据库配置是否正确')
reject()
} else {
logger.success('数据库连接成功')
this.connected = true
resolve()
}
})
})
}
/**
* 执行sql语句更新数据库
* @param {string} sql sql语句
* @param {string} componentName 组件名称
*/
query(sql) {
return new Promise((resolve, reject) => {
this.connection.query(sql, (error, result) => {
if (error) {
reject(error)
} else {
resolve(result)
}
})
})
}
/**
* 组件字段映射
* @param {string} field 字段名
* @returns 映射后的字段名
*/
fieldTransform(field) {
const fieldMap = {
docUrl: 'doc_url',
devMode: 'dev_mode',
schema: 'schema_fragment'
}
return fieldMap[field] || field
}
/**
* 格式化单引号
* @param {string} str 待格式化的字符串
* @returns 格式化后的字符串
*/
formatSingleQuoteValue(str) {
if (typeof str !== 'string') {
return str
}
return str.replace(/'/g, "\\'")
}
/**
* 校验组件数据是否有效
* @param {object} component 组件数据
* @returns boolean 校验组件字段是否失败false-有字段出错
*/
isValid(component, file) {
const longtextFields = ['name', 'npm', 'snippets', 'schema_fragment', 'configure', 'component_metadata']
return Object.entries(component).every(([key, value]) => {
if (longtextFields.includes(key) && value !== null && typeof value !== 'object') {
logger.error(`"${key}" 的值不是有效的JSON (${file})`)
return false
}
return true
})
}
/**
* 生成更新组件的sql语句
* @param {object} component 组件数据
* @returns 更新组件的sql语句
*/
updateComponent(component, file) {
const valid = this.isValid(component, file)
if (!valid) {
return
}
const values = []
let sqlContent = `update ${componentsTableName} set `
Object.keys(component).forEach((key) => {
const { [key]: value } = component
const fields = [
'version',
'name',
'component',
'icon',
'description',
'docUrl',
'screenshot',
'tags',
'keywords',
'devMode',
'npm',
'group',
'category',
'priority',
'snippets',
'schema',
'configure',
'public',
'framework',
'isOfficial',
'isDefault',
'tiny_reserved',
'tenant',
'createBy',
'updatedBy'
]
if (!fields.includes(key)) {
return
}
const field = this.fieldTransform(key)
let updateContent = ''
if (['id', 'component'].includes(field)) {
return
}
if (value === void 0) {
return
}
if (typeof value === 'string') {
const formatValue = this.formatSingleQuoteValue(value)
updateContent = `\`${field}\` = '${formatValue}'`
} else if (typeof field === 'number' || field === null) {
updateContent = `\`${field}\` = ${value}`
} else {
const formatValue = this.formatSingleQuoteValue(JSON.stringify(value))
updateContent = `\`${field}\` = '${formatValue}'`
}
values.push(updateContent)
})
sqlContent += values.join()
sqlContent += ` where component = '${component.component}';`
this.query(sqlContent, component.component)
.then(() => {
logger.success(`组件 ${component.component} 数据更新成功`)
})
.catch((error) => {
logger.error(`组件 ${component.component} 数据更新失败 ${error}`)
})
}
/**
* 新建的组件关联物料资产包
* @param {number} id 新建的组件id
*/
relationMaterialHistory(id) {
const uniqSql = `SELECT * FROM \`material_histories_components__user_components_mhs\` WHERE \`material-history_id\`=${materialHistoryId} AND \`user-component_id\`=${id}`
this.query(uniqSql).then((result) => {
if (!result.length) {
const sqlContent = `INSERT INTO \`material_histories_components__user_components_mhs\` (\`material-history_id\`, \`user-component_id\`) VALUES (${materialHistoryId}, ${id})`
this.query(sqlContent)
}
})
}
/**
* 生成新增组件的sql语句
* @param {object} component 组件数据
* @returns 新增组件的sql语句
*/
insertComponent(component, file) {
const valid = this.isValid(component, file)
if (!valid) {
return
}
const defaultName = {
zh_CN: component.component
}
const defaultNpm = {
package: '',
exportName: '',
version: '1.0.0',
destructuring: true
}
const defaultConfigure = {
loop: true,
condition: true,
styles: true,
isContainer: true,
isModal: false,
nestingRule: {
childWhiteList: '',
parentWhiteList: '',
descendantBlacklist: '',
ancestorWhitelist: ''
},
isNullNode: false,
isLayout: false,
rootSelector: '',
shortcuts: {
properties: ['value', 'disabled']
},
contextMenu: {
actions: ['create symbol'],
disable: ['copy', 'remove']
}
}
const {
version = '1.0.0',
name = defaultName,
component: componentName,
icon,
description,
docUrl,
screenshot,
tags,
keywords,
devMode = 'proCode',
npm = defaultNpm,
group,
category = 'general',
priority = 1,
snippets = [{}],
schema = {},
configure = defaultConfigure,
public: publicRight = 0,
framework = 'vue',
isOfficial = 0,
isDefault = 0,
tiny_reserved = 0,
tenant = 1,
createBy = 86,
updatedBy = 86
} = component
const values = `('${version}',
'${this.formatSingleQuoteValue(JSON.stringify(name))}',
'${componentName}',
'${icon}',
'${this.formatSingleQuoteValue(description)}',
'${docUrl}',
'${screenshot}',
'${tags}',
'${keywords}',
'${devMode}',
'${this.formatSingleQuoteValue(JSON.stringify(npm))}',
'${group}',
'${category}',
'${priority}',
'${this.formatSingleQuoteValue(JSON.stringify(snippets))}',
'${this.formatSingleQuoteValue(JSON.stringify(schema))}',
'${this.formatSingleQuoteValue(JSON.stringify(configure))}',
'${publicRight}',
'${framework}',
'${isOfficial}',
'${isDefault}',
'${tiny_reserved}',
'${tenant}',
'${createBy}',
'${updatedBy}'
);`
const sqlContent = `INSERT INTO ${componentsTableName} (version, name, component, icon, description, doc_url,
screenshot, tags, keywords, dev_mode, npm, \`group\`, \`category\`, priority, snippets,
schema_fragment, configure, \`public\`, framework, isOfficial, isDefault, tiny_reserved,
tenant, createdBy, updatedBy) VALUES ${values}`.replace(/\n/g, '')
this.query(sqlContent, componentName)
.then((result) => {
const id = result.insertId
logger.success(`组件 ${component.component} 数据新增成功`)
this.relationMaterialHistory(id)
})
.catch((error) => {
logger.success(`组件 ${component.component} 数据新增失败:${error}`)
})
}
/**
* 初始化数据库数据,判断是否已存在组件,不存在时执行新增组件
* @param {object} component 组件数据
*/
initDB(component) {
const selectSqlContent = `SELECT * FROM ${this.config.database}.${componentsTableName} WHERE component = '${component.component}'`
this.query(selectSqlContent)
.then((result) => {
if (!result.length) {
this.insertComponent(component)
}
})
.catch((error) => {
logger.success(`查询组件 ${component.component} 失败:${error}`)
})
}
/**
* 创建组件表
* @returns promise
*/
createUserComponentsTable() {
const sqlContent = `
CREATE TABLE ${componentsTableName} (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
version varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
name longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
component varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
icon varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
description varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
doc_url varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
screenshot varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
tags varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
keywords varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
dev_mode varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
npm longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
\`group\` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
category varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
priority int(11) NULL DEFAULT NULL,
snippets longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
schema_fragment longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
configure longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
createdBy int(11) NULL DEFAULT NULL,
updatedBy int(11) NULL DEFAULT NULL,
created_by int(11) NULL DEFAULT NULL,
updated_by int(11) NULL DEFAULT NULL,
created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
public int(11) NULL DEFAULT NULL,
framework varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
isOfficial tinyint(1) NULL DEFAULT NULL,
isDefault tinyint(1) NULL DEFAULT NULL,
tiny_reserved tinyint(1) NULL DEFAULT NULL,
tenant int(11) NULL DEFAULT NULL,
component_metadata longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
library int(11) NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE,
UNIQUE INDEX unique_component(createdBy, framework, component, version) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
`.replace(/\n/g, '')
return new Promise((resolve, reject) => {
this.query(sqlContent)
.then((result) => {
logger.success(`${componentsTableName} 创建成功`)
resolve(result)
})
.catch((error) => {
logger.success(`${componentsTableName} 创建失败:${error}`)
reject(error)
})
})
}
/**
* 初始化数据库的组件表
* @returns promise
*/
initUserComponentsTable() {
return new Promise((resolve, reject) => {
// 查询是否已存在表
this.query(`SHOW TABLES LIKE '${componentsTableName}'`)
.then((result) => {
if (result.length) {
// 已存在
resolve()
} else {
this.createUserComponentsTable()
.then(() => {
resolve()
})
.catch((err) => {
reject(err)
})
}
})
.catch((error) => {
reject(error)
})
})
}
}
export default MysqlConnection