This commit is contained in:
❀ » Cato Sweeney. ❀ » Console@the.bb
2025-11-27 16:14:04 +08:00
commit bb46cb3bcc
506 changed files with 499053 additions and 0 deletions

116
README.md Normal file
View File

@@ -0,0 +1,116 @@
# 🌐 无人机巡查平台
> 基于xMagic实现的前后端分离架构的无人机巡查平台
本项目用于实现对边坡状态的实时监控与数据分析,保障地质安全。系统采用前后端分离设计,前端使用 Vue.js后端基于 xScript 开发。
---
## 🗂 项目结构
```
CJGIS_UAVPatrol/
├── server/ # 后端服务目录
| └── init.js # 后端配置文件
| └── main.js # 后端服务入口
| └── init.sql # 数据库初始化脚本
├── ui/ # 前端项目目录
├── README.md # 项目说明文档
```
- **`server`**:后端服务逻辑、接口、数据库交互等。
- **`ui`**:前端界面,基于 Vue 框架开发。
---
## ⚙️ 配置说明
### 🖥 前端配置 (`ui/.env`)
配置后端 API 地址:
```env
VUE_APP_API_URL=http://172.16.17.32:8000
```
> 示例:若需切换环境,请修改此地址指向目标后端服务。
---
### 🔽 后端配置 (`server/init.js`)
配置数据库连接信息:
```env
MYSQL_HOST=172.16.17.32
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASS=your_password_here
MYSQL_DB=sucdri
```
✅ 确保数据库服务可访问,并已创建对应数据库。
---
## 🚀 指令说明
### 🎨 前端 (`ui/` 目录下执行)
| 命令 | 说明 |
|--------------------|--------------------|
| `npm install` | 安装依赖 |
| `npm run dev` | 启动开发服务器(测试) |
| `npm run serve` | 启动生产环境服务 |
| `npm run build` | 打包编译项目 |
> 推荐开发时使用 `dev`,部署前使用 `build` 生成静态资源。
---
### ⚙️ 后端 (`server/` 目录下执行)
| 命令 | 说明 |
|--------------------|--------------------------|
| `./xe main.js` | 启动正式服务 |
| `./xe test.js` | 启动测试服务 |
> 确保数据库配置正确并能正常连接。
---
## 🌍 部署访问
🔗 **当前部署地址**
👉 [https://shkc.p-q.co/app/panel/#/login](https://shkc.p-q.co/app/panel/#/login)
🔐 **默认登录账号**
- **账户**`root`
- **密码**`LMxeon5x`
---
## 📝 注意事项
- 请确保前后端服务在同一网络环境下互通。
- 生产部署建议使用 Nginx 反向代理前端PM2 托管后端服务。
- 定期备份数据库,防止数据丢失。
---
## 📂 技术栈(建议补充)
- **前端**Vue.js, Vue Router, Axios, Element UI
- **后端**Node.js, Express/Koa, MySQL, Sequelize/Knex
- **部署**Nginx, PM2, Docker
---
> 💡 如有疑问或问题,请联系项目负责人或查阅内部文档。
---
📌 *Version: 1.0.0*
📅 *Last Updated: 2025-04-05*
---

5
server/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/app
/uploads
/.ssl
xe
*.log

20
server/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# 使用 Alpine Linux 作为基础镜像
FROM alpine:latest
# 设置工作目录
WORKDIR /workspace
# 下载 xe 文件到当前目录
RUN wget -O /usr/bin/xe https://xmagic-1313201839.cos.ap-shanghai.myqcloud.com/xe
# 给 xe 文件赋予可执行权限
RUN chmod +x /usr/bin/xe
# 将本地当前目录下的所有文件拷贝到容器的 /workspace
COPY . .
# 开放 80 和 443 端口
EXPOSE 80 443
# 执行 main.js
CMD ["xe", "main.js"]

View File

@@ -0,0 +1,21 @@
// -------------- 获取所有api数据
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const res = SQL.query('system_sql', 'SELECT * FROM sys_apis WHERE delete_time IS NULL');
if (res) {
// 格式化数据
const newResult = res.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return newObj;
});
if (newResult) {
return okMsg(newResult);
}
}
return errMsg(404, '查询失败');
}

View File

@@ -0,0 +1,15 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().query;
const res = SQL.query(
DB_NAME,
'SELECT rule_id,role_id,role_key,method,patterns FROM casbin_rule WHERE delete_time IS NULL AND role_key = ?',
data.roleKey
);
if (res) {
return okMsg(res);
}
return errMsg(404, '查询失败');
}

View File

@@ -0,0 +1,42 @@
// ==========================================================================
// 数据表sys_apis - 系统接口数据
// ==========================================================================
TABLE_NAME = 'sys_apis';
COLUMN_KEY = 'id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'description') {
placeholders.push(`description LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'method') {
placeholders.push(`method = ?`);
tempParams.push(condition[item]);
} else if (item == 'path') {
placeholders.push(`path = ?`);
tempParams.push(condition[item]);
} else if (item == 'apiGroup') {
placeholders.push(`api_group = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return base_curd();
}

View File

@@ -0,0 +1,22 @@
// ==========================================================================
// 所属模块OaPartner
// 生成日期2024/10/19 16:00:00
// 生成路径: /oa/partner/oa-partner/function.js
// 生成人xMagic
// 数据表oa_partner - OA伙伴表
// 备注: 这个文件夹是保留最初未被代码所替代的高度抽象化的CURD接口代码以供后续业务需要进行参考。
// ==========================================================================
response.headers.add('X-Script-Version', 'oa-partner-oa-partner-gen-hQkjChmMeUu')
// 取数据应用的编码
var tableId = 'hQkjChmMeUu'
// 具体操作的数据库名称(数据源名称)
DB_NAME = 'system_sql'
// 具体操作的表名称
TABLE_NAME = 'oa_partner'
// 主要用于检索匹配的字段名称,一般为主键的名称。
COLUMN_KEY = 'partner_id'
// 后续逻辑引用sqlBaseCurd进行数据请求处理
'use api/dm/sql/sqlBaseCurd.js'

View File

@@ -0,0 +1,40 @@
// ==========================================================================
// 数据表oa_posts - 系统接口数据
// 备注这个文件夹是保留最初未被代码所替代的高度抽象化的CURD接口代码以供后续业务需要进行参考。
// ==========================================================================
TABLE_NAME = 'oa_posts';
COLUMN_KEY = 'post_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'postName') {
placeholders.push(`post_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'postCode') {
placeholders.push(`post_id = ?`);
tempParams.push(condition[item]);
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return base_curd();
}

View File

@@ -0,0 +1,52 @@
// ==========================================================================
// 数据表sys_configs - 系统接口数据
// ==========================================================================
TABLE_NAME = 'sys_configs';
COLUMN_KEY = 'config_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'title') {
placeholders.push(`title LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'name') {
placeholders.push(`name = ?`);
tempParams.push(condition[item]);
} else if (item == 'isSystem') {
placeholders.push(`is_system = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
break;
case 'POST':
case 'PUT':
case 'DELETE':
SQL.cache.remove( // 清除缓存
'system_sql',
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
);
break;
}
return base_curd();
}

View File

@@ -0,0 +1,10 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
SQL.cache.remove( // 清除缓存
'system_sql',
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
);
return okMsg(phone)
}

View File

@@ -0,0 +1,35 @@
response.headers.add('X-Script-Version', 'runtime-cache-1.0')
function del() {
var script = payload.get().query['path[]']
if (script[0] == '*') {
runtime.cache.purge()
return ``
}
for (var i = 0; i < script.length; i++) {
runtime.cache.remove(script[i])
}
}
function get() {
var script = payload.get().query['path']
if (!script || script == '' || script == '*') {
return okMsg(runtime.cache.list())
}
return runtime.cache.get(script)
}
function main() {
// 验证用户权限
// 'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return get()
case 'DELETE':
return del()
default:
response.status.notFound()
return `404 page not found`
}
}

View File

@@ -0,0 +1,100 @@
response.headers.add('X-Script-Version', 'runtime-redis-1.0')
function connect() {
var data = payload.get().body?.toObject()
if (data.name && data.uri) {
var result = REDIS.new(data.name, data.uri)
if (result == data.name) {
return okMsg("连接成功")
}
return errMsg(507, "连接失败")
}
return errMsg(507, '参数错误')
}
function get() {
var data = payload.get().body?.toObject()
if (data.name && data.key) {
var result = REDIS.get(data.name, data.key)
if (result) {
return okMsg(result)
}
return errMsg(507, "查询失败")
}
return errMsg(507, '参数错误')
}
function set() {
var data = payload.get().body?.toObject()
if (data.name && data.key && data.value) {
var result = REDIS.set(data.name, data.key, data.value, data.ttl ? data.ttl : -1)
if (result) {
return okMsg(result)
}
return errMsg(507, "赋值失败")
}
return errMsg(507, '参数错误')
}
function del() {
var data = payload.get().body?.toObject()
if (data.name && data.sql) {
var result = REDIS.del(data.name, data.key)
if (result) {
return okMsg(result)
}
return errMsg(507, "查询失败")
}
return errMsg(507, '参数错误')
}
function keys() {
var data = payload.get().body?.toObject()
if (data.name && data.sql) {
var result = REDIS.keys(data.name, data.pattern)
if (result) {
return okMsg(result)
}
return errMsg(507, "查询失败")
}
return errMsg(507, '参数错误')
}
function flush() {
var script = payload.get().query['name[]']
for (var i = 0; i < script.length; i++) {
REDIS.flush(script[i])
}
return okMsg("操作成功")
}
function close() {
var script = payload.get().query['name[]']
for (var i = 0; i < script.length; i++) {
REDIS.close(script[i])
}
return okMsg("操作成功")
}
function list() {
return okMsg(REDIS.list())
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list()
case 'POST':
return keys()
case 'PUT':
return connect()
case 'DELETE':
return close()
default:
response.status.notFound()
return `404 page not found`
}
}

View File

@@ -0,0 +1,68 @@
response.headers.add('X-Script-Version', 'runtime-sql-1.0')
function connect() {
var data = payload.get().body?.toObject()
if (data.name && data.type && data.uri) {
var result = SQL.new(data.name, data.type, data.uri, data.cache_size ? data.cache_size : 0)
if (result == data.name) {
return okMsg("连接成功")
}
return errMsg(507, "连接失败")
}
return errMsg(507, '参数错误')
}
function query() {
var data = payload.get().body?.toObject()
if (data.name && data.sql) {
var result = SQL.query(data.name, data.sql)
if (result) {
return okMsg(result)
}
return errMsg(507, "查询失败")
}
return errMsg(507, '参数错误')
}
function exec() {
var data = payload.get().body?.toObject()
if (data.name && data.sql) {
var result = SQL.exec(data.name, data.sql)
if (result) {
return okMsg(result)
}
return errMsg(507, "执行失败")
}
return errMsg(507, '参数错误')
}
function close() {
var script = payload.get().query['name[]']
for (var i = 0; i < script.length; i++) {
SQL.close(script[i])
}
return okMsg("操作成功")
}
function list() {
return okMsg(SQL.list())
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list()
case 'POST':
return query()
case 'PUT':
return connect()
case 'DELETE':
return close()
default:
response.status.notFound()
return `404 page not found`
}
}

View File

@@ -0,0 +1,26 @@
const params = formatSingleParams()
'use api/core/runtime/database/sql/tables.js'
// DB_NAME = 'system_sql'
// TABLE_NAME = ''
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
DB_NAME = payload.get().query.db_name
TABLE_NAME = payload.get().query.table_name
if (!TABLE_NAME) {
TABLE_NAME = payload.get().path.split("/").pop();
}
let info = getTableCollums()
let result = []
info.forEach(t => {
const newObj = Object.keys(t).reduce((acc, key) => {
acc[key.toLowerCase()] = t[key];
return acc;
}, {});
result.push(newObj)
});
return okMsg(result)
}

View File

@@ -0,0 +1,81 @@
// 获取数据库所有表信息
'use api/dm/sql/formatCondition.js'
function getTablesInfoStmt() {
// const placeholdersObj = formatCondition();
// const newPlaceholders = placeholdersObj.placeholders;
// console.log(placeholdersObj)
const tableName = payload.get().query?.tableName;
if (tableName) {
// const tempParams = placeholdersObj.tempParams;
return {
// query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND ${newPlaceholders} LIMIT ?,?;`,
query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE '%${tableName}%' LIMIT ?,?;`,
// params: [...tempParams, (params.pageNum - 1) * params.pageSize, params.pageSize],
params: [(params.pageNum - 1) * params.pageSize, params.pageSize],
};
} else {
return {
query: `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() LIMIT ?,?`,
params: [(params.pageNum - 1) * params.pageSize, params.pageSize],
};
}
}
// 列表查询总数据
function genCountTotal() {
// const placeholdersObj = formatCondition();
// const newPlaceholders = placeholdersObj.placeholders;
const tableName = payload.get().query?.tableName;
if (tableName) {
// const tempParams = placeholdersObj.tempParams;
return {
query: `SELECT COUNT(*) AS count FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE '%${tableName}%'`,
params: [],
// params: [...tempParams],
};
} else {
return {
query: `SELECT COUNT(*) AS count FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE()`,
params: [],
};
}
}
function getTablesInfo() {
let stmt = getTablesInfoStmt()
let countQuery = genCountTotal()
var dataRes = SQL.query(DB_NAME, stmt.query, ...stmt.params)
var countRes = SQL.query(DB_NAME, countQuery.query, ...countQuery.params)
if (dataRes) {
return { result: dataRes, count: Number(countRes[0]?.count) }
}
return undefined
}
function isExistTable() {
var result = SQL.query(DB_NAME, `SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, TABLE_NAME)
return result && result.length
}
function getTableInfo() {
let isExist = isExistTable()
if (isExist) {
var result = SQL.query(DB_NAME, `SELECT TABLE_NAME, TABLE_COMMENT, TABLE_ROWS, CREATE_TIME, UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`, TABLE_NAME)
if (result) {
return result
}
}
return undefined
}
function getTableCollums() {
let isExist = isExistTable()
if (isExist) {
var result = SQL.query(DB_NAME, `SHOW FULL COLUMNS FROM ${TABLE_NAME};`)
if (result) {
return result
}
}
return undefined
}

View File

@@ -0,0 +1,26 @@
const params = formatSingleParams();
'use api/core/runtime/database/sql/tables.js';
'use api/lib/camelAndUnderscore.js';
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let db_name = payload.get().query.db_name;
if (db_name) {
DB_NAME = db_name;
}
let tables = getTablesInfo();
let result = [];
tables.result.forEach((t) => {
const newObj = Object.keys(t).reduce((acc, key) => {
acc[underscoreToCamel(key.toLowerCase())] = t[key];
return acc;
}, {});
result.push(newObj);
});
return okMsg({ data: result, count: tables.count });
}

View File

@@ -0,0 +1,31 @@
response.headers.add('X-Script-Version', 'runtime-thread-1.0')
function stop() {
var script = payload.get().query['path[]']
if (script[0] == '*') {
runtime.thread.stopAll()
return ``
}
for (var i = 0; i < script.length; i++) {
runtime.thread.stop(script[i])
}
}
function list() {
return JSON.stringify(runtime.thread.list())
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list()
case 'DELETE':
return stop()
default:
response.status.notFound()
return `404 page not found`
}
}

View File

@@ -0,0 +1,37 @@
// --------sys_dict_data 添加字典配置标签
COLUMN_KEY = 'dict_code';
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().body?.toObject();
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = [];
columnsArr.push(COLUMN_KEY)
columnsArr.push('proprietor_id')
columnsArr.push('proprietor_name')
dataArr.push(ex.suid().base58())
dataArr.push(USER_INFO.user_id)
dataArr.push(USER_INFO.nickname)
dataArr.forEach(() => {
placeholders.push('?');
});
placeholders.join(',');
const columnsStr = columnsArr.join(',');
const query = `INSERT INTO sys_dict_data (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec('system_sql', query, ...dataArr);
if (res) {
return okMsg(res);
}
return errMsg(507, '添加失败');
}

View File

@@ -0,0 +1,12 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let filter = payload.get().path.split('/').pop();
const query = `UPDATE sys_dict_data SET delete_time = NOW() WHERE dict_code = ?`;
const res = SQL.exec('system_sql', query, filter);
if (res) {
return okMsg(res);
}
return errMsg(507, '删除失败');
}

View File

@@ -0,0 +1,69 @@
// --------sys_dict_data 获取字典配置详情
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'dictLabel') {
placeholders.push(`dict_label LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'dictType') {
// placeholders.push(`dict_type LIKE ?`);
// tempParams.push('%' + condition[item] + '%');
placeholders.push(`dict_type = ?`);
tempParams.push(condition[item]);
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function selectData(condition) {
const newPlaceholders = getPlaceholders(condition).placeholders;
if (newPlaceholders) {
const tempParams = getPlaceholders(condition).tempParams;
return {
query: `SELECT * FROM sys_dict_data WHERE ${newPlaceholders} AND delete_time IS NULL `,
params: [...tempParams],
};
} else {
return {
query: `SELECT * FROM sys_dict_data WHERE delete_time IS NULL`,
params: [],
};
}
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().query;
// 格式化这个queryS参数
let newData = {};
Object.keys(data).forEach((item) => {
newData[item] = data[item][0];
});
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
// 格式化数据
const newResult = result.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return newObj;
});
console.log("newResult-----",newResult)
return okMsg(newResult);
}

View File

@@ -0,0 +1,14 @@
// ===========
// sys_dict_data 更新字典配置详情
// ===========
TABLE_NAME = 'sys_dict_data';
COLUMN_KEY = 'dict_code';
'use api/lib/sql.js';
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return sql_update();
}

View File

@@ -0,0 +1,38 @@
// ==========================================================================
// 数据表sys_dict_types - 系统字典数据
// ==========================================================================
TABLE_NAME = 'sys_dict_types';
COLUMN_KEY = 'dict_id';
TABLE_FIELDS = '*'
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'dictName') {
placeholders.push(`dict_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'dictType') {
placeholders.push(`dict_type LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return base_curd();
}

View File

@@ -0,0 +1,89 @@
// ==========================================================================
// 所属模块DevGenTableColumns
// 生成日期2024-02-20 20:43:57 +0800 CST
// 生成路径: /dm/cols/function.js
// 生成人xScript_Engine
// 数据表dm_table_columns - 代码生成字段
// ==========================================================================
response.headers.add('X-Script-Version', 'core-gen-cols-gen-147')
// ===== 获取dm_table_columns表所有数据
TABLE_NAME = 'dm_table_columns';
COLUMN_KEY = 'table_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'tableId') {
placeholders.push(`tableId = ?`);
tempParams.push(condition[item]);
} else if (item == 'tableName') {
placeholders.push(`table_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'columnComment') {
placeholders.push(`column_comment LIKE ?`);
tempParams.push('%' + condition[item] + '%');
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function update() {
const data = payload.get().body?.toObject();
delete data.createTime;
delete data.updateTime;
const newData = {};
let field
Object.keys(data).forEach((name) => {
field = Object(TABLE_STRUCT).find(data => data.field_name === data).column_name
newData[field] = data[name];
});
let filter = payload.get().path.split('/').pop();
const columnsArr = Object.keys(newData);
let dataArr = [];
const placeholders = [];
columnsArr.forEach((item) => {
if (newData[item]) {
placeholders.push(`${item} =?`);
dataArr.push(newData[item]);
} else {
placeholders.push(`${item} = NULL`);
}
})
const newPlaceholders = placeholders.join(',');
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
const res = SQL.exec(DB_NAME, query, ...[...dataArr, filter]);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'PUT':
return update();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,89 @@
// 创建数据表的信息
//
'use api/core/runtime/database/sql/tables.js';
'use api/lib/camelAndUnderscore.js';
// 根据sql列类型获取html类型
function getHtmlTypeFromColumnType(columnType) {
// const ColumnTypeStr = ['char', 'varchar', 'narchar', 'varchar2', 'tinytext', 'text', 'mediumtext', 'longtext']; // to "input";
const ColumnTypeTime = ['datetime', 'time', 'date', 'timestamp', 'timestamptz']; // to "datetime";
// const ColumnTypeNumber = [
// 'smallint',
// 'mediumint',
// 'int',
// 'int2',
// 'int4',
// 'int8',
// 'number',
// 'integer',
// 'numeric',
// 'bigint',
// 'float',
// 'float4',
// 'float8',
// 'double',
// 'decimal',
// ]; // to "input";
const ColumnTypeBool = ['tinyint', 'bool', 'boolean']; // to "switch";
const dataType = columnType.toLowerCase();
if (ColumnTypeTime.includes(dataType)) {
return 'datetime';
} else if (ColumnTypeBool.includes(dataType)) {
return 'switch';
} else {
return 'input';
}
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let tableId = payload.get().path.split('/').pop();
TABLE_STRUCT = SQL.query('system_sql', 'SELECT * FROM dm_table_columns WHERE table_id = ?', tableId);
if (TABLE_STRUCT.length == 0) {
return errMsg(507, '表结构为空');
}
DB_NAME = SQL.query('system_sql', 'SELECT db_name FROM dm_tables WHERE table_id = ?', tableId)[0].db_name;
TABLE_NAME = TABLE_STRUCT[0].table_name;
let tableCollums = getTableCollums();
// let fieldName = underscoreToCamel(col.Field);
const temp = [];
for (let i = 0; i < tableCollums.length; i++) {
let col = tableCollums[i];
// 如果TABLE_STRUCT有元素的column_name和col.Field相同则跳过
if (TABLE_STRUCT.find((item) => item.column_name == col.Field)) {
continue;
}
let stmt =
'INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_increment, html_type, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
// tid, sort, tname , cname, ccomment, ctype, ckey, jfield, ispk, isrequired, isinc, htype oid, own
let params = [
ex.suid().base58(),
tableId,
i + 1,
TABLE_NAME,
col.Field,
col.Comment,
col.Type,
col.Key,
underscoreToCamel(col.Field),
col.Key == 'PKI' ? 1 : 0,
col.Null == 'NO' ? 1 : 0,
col.Extra.includes('auto_increment') ? 1 : 0,
getHtmlTypeFromColumnType(col.Type),
USER_INFO.user_id,
USER_INFO.nickname,
];
SQL.push('system_sql', stmt, ...params);
temp.push(params);
}
return okMsg('创建表信息成功');
}

View File

@@ -0,0 +1,25 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let tableId = payload.get().path.split('/').pop()
let newTableId = ex.suid().base58()
let query = `INSERT INTO dm_tables (table_id, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name) SELECT ?, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name FROM dm_tables WHERE table_id =?`
let res = SQL.exec('system_sql', query, newTableId, tableId)
if (!res) {
return errMsg(507, '创建表信息失败')
}
query = `SELECT column_id FROM dm_table_columns WHERE table_id =?`;
res = SQL.query('system_sql', query, tableId)
let newColumnId
res.forEach((columnId) => {
newColumnId = ex.suid().base58()
query = `INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_insert, is_show, is_edit, is_list, is_query, is_acl, is_group, calc_type, is_order, query_type, search_value, default_value, is_increment, html_type, html_width, dict_type, proprietor_id, proprietor_name) SELECT '${newColumnId}', '${newTableId}', sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_insert, is_show, is_edit, is_list, is_query, is_acl, is_group, calc_type, is_order, query_type, search_value, default_value, is_increment, html_type, html_width, dict_type, proprietor_id, proprietor_name FROM dm_table_columns WHERE column_id =?`
// console.log(query, columnId.column_id)
SQL.exec('system_sql', query, columnId.column_id)
})
return okMsg(newTableId)
}

88
server/api/dm/function.js Normal file
View File

@@ -0,0 +1,88 @@
// ==========================================================================
// 所属模块DevGenTables
// 生成日期2024-02-20 21:43:27 +0800 CST
// 生成路径: apps/system/function.js
// 生成人xScript_Engine
// 数据表dm_tables - DevGenTables
// ==========================================================================
TABLE_NAME = 'dm_tables';
COLUMN_KEY = 'table_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'tableName') {
placeholders.push(`table_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'tableId') {
placeholders.push(`table_id = ?`);
tempParams.push(condition[item]);
} else if (item == 'tableComment') {
placeholders.push(`table_comment LIKE ?`);
tempParams.push('%' + condition[item] + '%');
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function update() {
const data = payload.get().body?.toObject();
// 验证data.tableName 和 data.columnName 只能有字母数字下划线
if (!/^[a-zA-Z0-9_]+$/.test(data.tableName) || !/^[a-zA-Z0-9_]+$/.test(data.columnName) || !/^[a-zA-Z0-9_]+$/.test(data.dbName)) {
return errMsg(405, '不支持的数据');
}
delete data.createTime;
delete data.updateTime;
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
let filter = payload.get().path.split('/').pop();
const columnsArr = Object.keys(newData);
let dataArr = [];
const placeholders = [];
columnsArr.forEach((item) => {
if (newData[item]) {
placeholders.push(`${item} = ?`);
dataArr.push(newData[item]);
} else {
placeholders.push(`${item} = NULL`);
}
});
const newPlaceholders = placeholders.join(',');
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'PUT':
return update();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,134 @@
// 创建数据表的信息
//
'use api/core/runtime/database/sql/tables.js';
'use api/lib/camelAndUnderscore.js';
// 根据sql列类型获取html类型
function getHtmlTypeFromColumnType(columnType) {
// const ColumnTypeStr = ['char', 'varchar', 'narchar', 'varchar2', 'tinytext', 'text', 'mediumtext', 'longtext']; // to "input";
const ColumnTypeTime = ['datetime', 'time', 'date', 'timestamp', 'timestamptz']; // to "datetime";
// const ColumnTypeNumber = [
// 'smallint',
// 'mediumint',
// 'int',
// 'int2',
// 'int4',
// 'int8',
// 'number',
// 'integer',
// 'numeric',
// 'bigint',
// 'float',
// 'float4',
// 'float8',
// 'double',
// 'decimal',
// ]; // to "input";
const ColumnTypeBool = ['tinyint', 'bool', 'boolean']; // to "switch";
const dataType = columnType.toLowerCase();
if (ColumnTypeTime.includes(dataType)) {
return 'datetime';
} else if (ColumnTypeBool.includes(dataType)) {
return 'switch';
} else {
return 'input';
}
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let data = payload.get().body?.toObject();
if (data.db) {
DB_NAME = data.db;
}
TABLE_NAME = data.tables;
let dbInfo = SQL.query('system_sql', 'SELECT * FROM sys_database WHERE name = ?;', DB_NAME);
if (!dbInfo.length) {
return errMsg(507, '数据库不存在');
}
dbInfo = dbInfo[0];
// TABLE_NAME 可能由多个组成,采用, 分割
let tables = TABLE_NAME.split(',');
tables.forEach((table) => {
let pkColumn = '';
let tableInfo = getTableInfo();
let tableCollums = getTableCollums();
tableCollums.forEach((pk) => {
// pk_column 是取Key为PKI的Field值
if (pk.Key == 'PRI') {
pkColumn = pk.Field;
return pkColumn;
}
});
let moduleName = table.replace('_', '-');
let businessName = moduleName.replace(/^.*?-/, '');
let packageName = capitalizeFirstLetter(underscoreToCamel(table));
let stmt =
'INSERT INTO dm_tables (db_id, db_name, db_comment, table_id, table_name, table_comment, class_name, module_name, business_name, function_name, function_author, pk_column, pk_field_name, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
const tableId = ex.suid().base58()
let res = SQL.exec(
'system_sql',
stmt,
dbInfo.db_id,
dbInfo.name,
dbInfo.comment,
tableId,
table,
tableInfo[0]['TABLE_COMMENT'],
packageName,
moduleName,
businessName,
capitalizeFirstLetter(businessName),
USER_INFO.username + '-' + USER_INFO.nickname,
pkColumn,
underscoreToCamel(pkColumn),
USER_INFO.user_id,
USER_INFO.nickname,
);
if (!res) {
return errMsg(507, '创建表信息失败');
}
// let fieldName = underscoreToCamel(col.Field);
const temp = [];
for (let i = 0; i < tableCollums.length; i++) {
let col = tableCollums[i];
let stmt =
'INSERT INTO dm_table_columns (column_id, table_id, sort, table_name, column_name, column_comment, column_type, column_key, field_name, is_pk, is_required, is_increment, html_type, proprietor_id, proprietor_name) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);';
// tid, sort, tname , cname, ccomment, ctype, ckey, jfield, ispk, isrequired, isinc, htype oid, own
let params = [
ex.suid().base58(),
tableId,
i + 1,
TABLE_NAME,
col.Field,
col.Comment,
col.Type,
col.Key,
underscoreToCamel(col.Field),
col.Key == 'PKI' ? 1 : 0,
col.Null == 'NO' ? 1 : 0,
col.Extra.includes('auto_increment') ? 1 : 0,
getHtmlTypeFromColumnType(col.Type),
USER_INFO.user_id,
USER_INFO.nickname,
];
SQL.push('system_sql', stmt, ...params);
temp.push(params);
}
});
return okMsg('创建表信息成功');
}

89
server/api/dm/sql/data.js Normal file
View File

@@ -0,0 +1,89 @@
// 获取数据
function sqlGenSelect() {
let userAccess
// 判断用户表权限
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
let udr = []
udr.push(`'${USER_INFO.user_id}'`)
udr.push(`'${USER_INFO.role_id}'`)
udr.push(`'${USER_INFO.organization_id}'`)
let columnIds = []
TABLE_STRUCT.forEach(item => {
if (item.is_acl == 1) {
columnIds.push(`'${item.column_id}'`)
}
if (item.search_value) {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
}
})
userAccess = SQL.query(
DB_NAME,
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level > 0 AND delete_time IS NULL;`,
TABLE_INFO.table_id
)
} else {
TABLE_STRUCT.forEach(item => {
if (item.search_value) {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
}
})
}
let value = payload.get().query[COLUMN_KEY]
// 兼容 /user/1 这种通过 path 传值的方式
if (!value) {
// 声明是否使用上一级的脚本
var useParent = scriptPath != payload.get().path.substring(1)
if (useParent) {
value = payload.get().path.split("/").pop();
} else {
return sqlGenList();
}
}
var cols = []
for (var i = 0; i < TABLE_STRUCT.length; i++) {
if (TABLE_STRUCT[i].is_show == 1) {
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
continue
}
cols.push(TABLE_STRUCT[i].column_name + ' as ' + TABLE_STRUCT[i].field_name)
}
}
const chooseData = cols.join(',');
// const chooseData = "*";
stmt = `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ? ${searchStr}`
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook)
}
SQL_STMTS.push(stmt)
result = SQL.query(
DB_NAME,
stmt, value
)
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
// let result = []
// res.forEach(t => {
// const newObj = Object.keys(t).reduce((acc, key) => {
// acc[underscoreToCamel(key.toLowerCase())] = t[key];
// return acc;
// }, {});
// result.push(newObj)
// });
if (result) {
return okMsg(result)
}
return errMsg(404, '查询失败')
}

View File

@@ -0,0 +1,31 @@
// 删除数据
function sqlGenDelete() {
const findData = TABLE_STRUCT.find(item => item.column_name == COLUMN_KEY) || {}
const parName = findData?.field_name || underscoreToCamel(COLUMN_KEY)
const ids = payload.get().query[parName + '[]']
const tempParams = []
for (var i = 0; i < ids.length; i++) {
tempParams.push('?')
}
stmt = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} IN (${tempParams.join(',')})`
if (TABLE_INFO.before_hook) {
result = runtime.call(TABLE_INFO.before_hook)
}
// let sql = `DELETE FROM ${TABLE_NAME} WHERE ${COLUMN_KEY} IN (${tempParams.join(',')})`
SQL_STMTS.push(stmt)
result = SQL.exec(
DB_NAME,
stmt, ...ids
)
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
if (result) {
return okMsg(result)
}
return errMsg(507, '参数错误')
}

View File

@@ -0,0 +1,79 @@
// 格式化筛选条件
function formatCondition() {
if (Object.keys(params).length > 0) {
const placeholders = [];
const tempParams = [];
var cols = []
let mapCols = {}
TABLE_STRUCT.forEach(item => {
if (item.is_query == 1) {
cols.push(item.column_name)
if (item.field_name) {
mapCols[`${item.field_name}`] = item.column_name
}
}
})
// 将存在别名的params转换
let coverParams = {}
Object.keys(params).forEach(item => {
if (mapCols[item]) {
coverParams[mapCols[item]] = params[item]
} else {
coverParams[item] = params[item]
}
})
Object.keys(coverParams).forEach(key => {
let col_name = camelToUnderscore(key)
if (cols.includes(col_name)) {
var row = {}
TABLE_STRUCT.find(item => {
if (item.column_name == col_name) {
row = item
}
})
if (coverParams[key]) {
// if (row.query_type == "NaN" || row.query_type == "VAL") {
// placeholders.push(`${col_name} ${sql_operator[row.query_type]}`);
// }
// if (row.query_type == "LIKE") {
// tempParams.push('%' + coverParams[key] + '%');
// } else {
// tempParams.push(coverParams[key]);
// }
switch (row.query_type) {
case "NAN":
case "VAL":
placeholders.push(`${col_name} ${sql_operator[row.query_type]}`);
break;
case "LIKE":
placeholders.push(`${col_name} ${sql_operator[row.query_type]} ?`);
tempParams.push('%' + coverParams[key] + '%');
break;
case "BT":
if (coverParams[key].includes(',')) {
placeholders.push(`${col_name} BETWEEN ? AND ?`);
let rangValue = coverParams[key].split(',')
tempParams.push(rangValue[0]);
tempParams.push(rangValue[1]);
}
break;
default:
placeholders.push(`${col_name} ${sql_operator[row.query_type]} ?`);
tempParams.push(coverParams[key]);
break;
}
}
}
})
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
}
}
}

153
server/api/dm/sql/insert.js Normal file
View File

@@ -0,0 +1,153 @@
// 添加数据
function sqlGenInsert() {
var data = payload.get().body?.toObject()
if (!data || (data && !Object.keys(data).length)) {
return errMsg(404, '未传入参数')
}
let userAccess
// 判断用户表权限
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
let udr = []
udr.push(`'${USER_INFO.user_id}'`)
udr.push(`'${USER_INFO.role_id}'`)
udr.push(`'${USER_INFO.organization_id}'`)
let columnIds = []
TABLE_STRUCT.forEach(item => {
if (item.is_acl == 1) {
columnIds.push(`'${item.column_id}'`)
}
})
userAccess = SQL.query(
DB_NAME,
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level = 2 AND delete_time IS NULL;`,
TABLE_INFO.table_id
)
}
var insertCols = []
for (var i = 0; i < TABLE_STRUCT.length; i++) {
if (TABLE_STRUCT[i].is_insert == 1) {
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
continue
}
insertCols.push(TABLE_STRUCT[i].column_name)
}
}
var dataArr = []
var dataKeyArr = Object.keys(data)
var dataId = ex.suid().base58()
var cols = []
var values = []
// var cols = [COLUMN_KEY]
// var cols = [COLUMN_KEY, 'proprietor_id', 'proprietor_name']
// var values = [`'${dataId}'`]
// var values = [`'${dataId}'`, `'${USER_INFO.user_id}'`, `'${USER_INFO.nickname}'`]
let newItem
// 如果table_struct 有proprietor_id字段则自动添加
if (TABLE_STRUCT.find(item => item.column_name === 'proprietor_id')) {
cols.push('proprietor_id')
values.push(`'${USER_INFO.user_id}'`)
cols.push('proprietor_name')
values.push(`'${USER_INFO.nickname}'`)
}
for (let i = 0; i < dataKeyArr.length; i++) {
newItem = TABLE_STRUCT.find(itemT => itemT.field_name === dataKeyArr[i])
if (!newItem) {
console.error('未找到字段:', dataKeyArr[i])
return errMsg(404, '未找到字段')
}
if (insertCols.includes(newItem.column_name)) {
cols.push(newItem.column_name)
values.push('?')
if (newItem.column_name == COLUMN_KEY) {
if (data[dataKeyArr[i]]) {
dataId = data[dataKeyArr[i]]
} else {
data[dataKeyArr[i]] = dataId
}
}
if (!data[dataKeyArr[i]] && newItem?.default_value) {
data[dataKeyArr[i]] = newItem.default_value
}
if (data[dataKeyArr[i]] == "suid") {
dataArr.push(ex.suid().base58())
}
dataArr.push(data[dataKeyArr[i]])
}
}
if (!cols.includes(COLUMN_KEY)) {
cols.push(COLUMN_KEY)
values.push('?')
dataArr.push(dataId)
}
for (var i = 0; i < TABLE_STRUCT.length; i++) {
if (TABLE_STRUCT[i].is_required == 1 && !cols.includes(TABLE_STRUCT[i].column_name)) {
return errMsg(507, `未填写必选项:${TABLE_STRUCT[i].column_name}`)
}
}
stmt = `INSERT INTO ${TABLE_NAME}(${cols.join(',')}) VALUES (${values.join(',')})`
if (TABLE_INFO.before_hook) {
result = runtime.call(TABLE_INFO.before_hook)
}
payload.add.header("sql_exec", stmt)
result = SQL.exec(DB_NAME, stmt, ...dataArr)
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
if (!result) {
console.error('添加失败')
return errMsg(507, '添加失败')
}
stmt = `SELECT access_id,level FROM dm_tables_acl WHERE table_id =? AND delete_time IS NULL`
if (TABLE_INFO.before_hook) {
result = runtime.call(TABLE_INFO.before_hook)
}
SQL_STMTS.push(stmt)
result = SQL.query('system_sql', stmt, TABLE_INFO.table_id)
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
if (result) {
result.forEach(item => {
switch (item.access_id) {
case 'self':
item.access_id = USER_INFO.user_id
break
case 'role':
item.access_id = USER_INFO.role_id
break
case 'dept':
item.access_id = USER_INFO.organization_id
break
}
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook)
}
SQL.push('system_sql', `INSERT INTO sys_data_acl (data_id,access_id,level,proprietor_id,proprietor_name) VALUES (?,?,?,?,?)`, dataId, item.access_id, item.level, USER_INFO.user_id, USER_INFO.nickname)
})
} else {
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook)
}
SQL.push('system_sql', `INSERT INTO sys_data_acl (data_id,access_id,level,proprietor_id,proprietor_name) VALUES (?,?,?,?,?)`, dataId, item.user_id, 3, USER_INFO.user_id, USER_INFO.nickname)
}
return okMsg(dataId)
}

210
server/api/dm/sql/list.js Normal file
View File

@@ -0,0 +1,210 @@
// 分页数据
function genSelectData(chooseData, placeholdersObj) {
const newPlaceholders = placeholdersObj.placeholders;
if (newPlaceholders != '') {
const tempParams = placeholdersObj.tempParams;
return {
query: `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE ${newPlaceholders}`,
params: [...tempParams],
};
} else {
return {
query: `SELECT ${chooseData} FROM ${TABLE_NAME} WHERE 1`,
params: [],
};
}
}
// 列表查询总数居
function genCountTotal(placeholdersObj) {
const newPlaceholders = placeholdersObj.placeholders;
if (newPlaceholders != '') {
const tempParams = placeholdersObj.tempParams;
return {
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE ${newPlaceholders}`,
params: [...tempParams],
};
} else {
return {
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE 1`,
params: [],
};
}
}
// 分页列表查询
function genListPage(chooseData, placeholdersObj) {
// 总数据
const ROWS_COUNT = genCountTotal(placeholdersObj);
ROWS_COUNT.query += searchStr
stmt = ROWS_COUNT.query
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook)
}
SQL_STMTS.push(stmt)
result = SQL.query(DB_NAME, stmt, ...ROWS_COUNT.params);
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
// 总数据
const count = result
// 分页数据
const LIST_DATA = genSelectData(chooseData, placeholdersObj);
if (parseInt(params.pageSize) > 1000) {
response.status.code(413)
return false
}
if (params.pageNum && params.pageSize) {
searchStr += ` LIMIT ${(params.pageNum - 1) * parseInt(params.pageSize)}, ${parseInt(params.pageSize)}`
}
LIST_DATA.query += searchStr
stmt = LIST_DATA.query
// console.log(LIST_DATA.query)
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook)
}
SQL_STMTS.push(stmt)
result = SQL.query(DB_NAME, stmt, ...LIST_DATA.params);
if (!result) return;
const coverRes = result.sort(compareSorts)
result = { result: coverRes, count: count[0].count, pageNum: params.pageNum, pageSize: params.pageSize }
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook)
}
return result;
}
// 获取sql查询列表
function sqlGenList() {
let userAccess // 判断用户表权限
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
let udr = []
udr.push(`'${USER_INFO.user_id}'`)
udr.push(`'${USER_INFO.role_id}'`)
udr.push(`'${USER_INFO.organization_id}'`)
let columnIds = []
TABLE_STRUCT.forEach(item => {
if (item.is_acl == 1) {
columnIds.push(`'${item.column_id}'`)
}
if (item.search_value) {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
}
if (item.query_type == "NAN" || item.query_type == "VAL") {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]}`
}
})
userAccess = SQL.query(
DB_NAME,
`SELECT DISTINCT column_name FROM dm_table_columns_acl WHERE column_id IN (${columnIds.split(',')}) AND access_id IN (${udr.split(',')}) AND level > 0 AND delete_time IS NULL;`,
TABLE_INFO.table_id
)
} else {
TABLE_STRUCT.forEach(item => {
if (item.search_value) {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]} '${item.search_value}'`
}
if (item.query_type == "NAN" || item.query_type == "VAL") {
searchStr += ` AND ${item.column_name} ${sql_operator[item.query_type]}`
}
})
}
var cols = []
var groups = []
var orders = []
var distincts = []
for (var i = 0; i < TABLE_STRUCT.length; i++) {
if (TABLE_STRUCT[i].is_list == 1) {
if (TABLE_STRUCT[i].is_acl == 1 && !userAccess.includes(TABLE_STRUCT[i].column_name)) {
continue
}
// 运算规则 0 SUM 1 COUNT 2 AVG 3 MAX 4 MIN 5 VARIANCE 6 STDDEV 7 GROUP_CONCAT
switch (TABLE_STRUCT[i].calc_type) {
case 0:
cols.push(`SUM(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 1:
cols.push(`COUNT(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 2:
cols.push(`AVG(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 3:
cols.push(`MAX(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 4:
cols.push(`MIN(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 5:
cols.push(`VARIANCE(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 6:
cols.push(`STDDEV(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
case 7:
cols.push(`GROUP_CONCAT(${TABLE_STRUCT[i].column_name}) as ${TABLE_STRUCT[i].field_name}`)
break
default:
cols.push(TABLE_STRUCT[i].column_name + ' as ' + TABLE_STRUCT[i].field_name)
break
}
if (TABLE_STRUCT[i].is_distinct == 1) {
distincts.push(`${TABLE_STRUCT[i].field_name}`)
}
if (TABLE_STRUCT[i].is_group == 1) {
groups.push(`${TABLE_STRUCT[i].field_name}`)
}
if (TABLE_STRUCT[i].is_order == 1) {
orders.push(`${TABLE_STRUCT[i].field_name}`)
}
}
}
const chooseData = cols.join(',');
if (chooseData == '') {
return errMsg(507, '未设置列表字段')
}
if (TABLE_INFO.is_distinct == 1) {
chooseData = `DISTINCT ${chooseData}`
}
distincts = distincts.join(',')
if (distincts != '') {
chooseData = `DISTINCT ${distincts}`
}
groups = groups.join(',')
if (groups != '') {
searchStr += ` GROUP BY ${groups}`
}
orders = orders.join(',')
if (orders != '') {
searchStr += ` ORDER BY ${orders} ${TABLE_INFO.order_type == 1 ? 'DESC' : 'ASC'}`
}
const placeholdersObj = formatCondition();
const res = genListPage(chooseData, placeholdersObj);
return okMsg(res);
}

94
server/api/dm/sql/sql.js Normal file
View File

@@ -0,0 +1,94 @@
// ==========================================================================
// 所属模块core - gen - sql
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: dm/sql/sql.js
// 生成人Cato
// 功能描述定义全局通用的SQL的CURD功能封装包含分页列表和模版查询、数据增删等等。注册于global.js内)
// ==========================================================================
// 系统的sql数据库连接名为 system_sql
// 连接测试数据库
// var test_sql = SQL.new(
// 'mydb',
// 'mysql',
// 'root:123456@tcp(127.0.0.1:3306)/test_db'
// )
var stmt
var result
let searchStr = ''
'use api/dm/struct/table.js'
'use api/dm/sql/utils.js'
'use api/dm/sql/data.js'
'use api/dm/sql/list.js'
'use api/dm/sql/delete.js'
'use api/dm/sql/insert.js'
'use api/dm/sql/update.js'
'use api/dm/sql/formatCondition.js'
'use api/lib/requestQueryFormatSingle.js'
'use api/lib/camelAndUnderscore.js'
// 基本的CURD功能
function base_curd() {
payload.set.header("Databus-Time", new Date().getTime())
let userAccess
// 判断用户表权限
if (TABLE_INFO.is_acl && !ALLOW_PUBLIC_ACCESS) {
let udr = []
udr.push(`'${USER_INFO.user_id}'`)
udr.push(`'${USER_INFO.organization_id}'`)
userAccess = SQL.query(
'system_sql',
`SELECT MAX(level) as level FROM dm_tables_acl WHERE table_id =? AND access_id IN (${udr.split(',')}) AND delete_time IS NULL;`,
TABLE_INFO.table_id
)
if (userAccess) {
userAccess = userAccess[0].level
} else {
return errMsg(403, '无权限')
}
}
switch (payload.get().method) {
case 'GET':
if (TABLE_INFO.is_acl && userAccess < 1) {
return errMsg(403, '无权限')
}
if (TABLE_INFO.is_acl && !getAclData(payload.get().query[COLUMN_KEY], 1)) {
return errMsg(403, '无权限')
}
return sqlGenSelect()
case 'PUT':
if (TABLE_INFO.is_acl && userAccess < 2) {
return errMsg(403, '无权限')
}
if (TABLE_INFO.is_acl && !getAclData(tableId, 1)) {
return errMsg(403, '无权限')
}
return sqlGenUpdate()
case 'POST':
if (TABLE_INFO.is_acl && userAccess < 2) {
return errMsg(403, '无权限')
}
return sqlGenInsert()
case 'DELETE':
if (TABLE_INFO.is_acl && userAccess < 3) {
return errMsg(403, '无权限')
}
if (TABLE_INFO.is_acl && !getAclData(tableId, 3)) {
return errMsg(403, '无权限')
}
return sqlGenDelete()
default:
response.status.notFound()
return `404 page not found`
}
}

View File

@@ -0,0 +1,34 @@
var params
function main() {
// APPID认证
'use api/lib/userPassport.js'
if (!payload.get().query.t) {
return errMsg(507, '数据源不存在')
}
var tableId = payload.get().query.t
let startTime = new Date().getTime()
TABLE_INFO = getTableMeta(tableId)
if (!TABLE_INFO) {
return errMsg(507, '数据源不存在')
}
DB_NAME = TABLE_INFO.db_name
TABLE_NAME = TABLE_INFO.table_name
COLUMN_KEY = TABLE_INFO.pk_column
ALLOW_PUBLIC_ACCESS = TABLE_INFO.is_public == 1
// 验证用户权限
'use api/user/acl/excerpt.js'
TABLE_STRUCT = getTableStruct(tableId)
// 格式化参数
params = formatSingleParams()
response.headers.add("Server-Timing", `Deserialization;dur=${new Date().getTime() - startTime}`)
return base_curd();
}

107
server/api/dm/sql/update.js Normal file
View File

@@ -0,0 +1,107 @@
// 修改数据
function sqlGenUpdate() {
const commitData = payload.get().body?.toObject();
let filter = payload.get().query[COLUMN_KEY];
if (filter == undefined) {
filter = payload.get().path.split("/").pop();
}
if (!filter) {
return errMsg(507, "缺少主键参数");
}
var oldData = SQL.query(
DB_NAME,
`SELECT * FROM ${TABLE_NAME} WHERE ${COLUMN_KEY} = ?`,
filter
);
if (!oldData || !oldData[0]) {
return errMsg(507, "未找到原始数据");
}
oldData = oldData[0];
const syncList = {};
const cols = [];
var linkBindColumn = {};
TABLE_STRUCT.forEach((item) => {
if (item.is_edit == 1) {
if (
item?.link_sync_type > 1 &&
item?.link_db_name &&
item?.link_table_name &&
item?.link_column_name
) {
linkBindColumn = TABLE_STRUCT.find(
(linkColumn) => linkColumn.column_name == item.link_bind_field
);
syncList[item.field_name] = {
db_name: item.link_db_name,
table_name: item.link_table_name,
self_field: item.link_bind_field,
link_field: item.link_column_name,
bind_field: linkBindColumn.link_column_name,
};
}
cols.push(item.column_name);
}
});
const updateArr = [];
const dataArr = [];
Object.keys(commitData).forEach((key) => {
const colName = camelToUnderscore(key);
if (cols.includes(colName)) {
if (commitData[key] === "null" || commitData[key] === null) {
updateArr.push(`${colName} = null`);
} else if (commitData[key] != oldData[key]) {
updateArr.push(`${colName} = ?`);
dataArr.push(commitData[key]);
}
}
});
// 处理同步更新
const syncData = handleSyncUpdate(syncList, commitData, oldData);
Object.keys(syncData).forEach((dbName) => {
payload.add.header("sql_exec", stmt);
SQL.transaction(dbName, syncData[dbName].join(";"))
// if (SQL.exec(dbName, syncData[dbName].join(";"))) {
// return errMsg(507, "同步更新失败");
// }
});
if (updateArr.length > 0) {
const stmt = `UPDATE ${TABLE_NAME} SET ${updateArr.join(",")} WHERE ${COLUMN_KEY} = ?`;
if (TABLE_INFO.before_hook) {
runtime.call(TABLE_INFO.before_hook);
}
payload.add.header("sql_exec", stmt);
const result = SQL.exec(DB_NAME, stmt, ...dataArr, filter);
if (TABLE_INFO.after_hook) {
runtime.call(TABLE_INFO.after_hook);
}
if (result) {
return okMsg(result);
}
}
return errMsg(507, "参数错误或无可更新字段");
}
// 同步关联修改数据
function handleSyncUpdate(syncList, commitData, oldData) {
const syncData = {};
Object.keys(syncList).forEach((key) => {
if (!syncData[syncList[key].db_name]) {
syncData[syncList[key].db_name] = [];
}
if (commitData[key] == "null" || commitData[key] == null) {
syncData[syncList[key].db_name].push(
`UPDATE ${syncList[key].table_name} SET ${syncList[key].link_field} = null WHERE ${syncList[key].bind_field} = '${oldData[syncList[key].self_field]}'`
);
} else if (commitData[key] != oldData[key]) {
syncData[syncList[key].db_name].push(
`UPDATE ${syncList[key].table_name} SET ${syncList[key].link_field} = '${commitData[key]}' WHERE ${syncList[key].bind_field} = '${oldData[syncList[key].self_field]}'`
);
}
});
return syncData;
}

View File

@@ -0,0 +1,38 @@
/**比较两个对象的 sort 属性*/
function compareSorts(a, b) {
// 如果 sort 属性为 null则将其视为无穷大
if (!a.sort || !b.sort) {
return 0; // 不参与排序
}
// 比较 sort 属性
return a.sort - b.sort;
}
const sql_operator = {
EQ: '=',
NE: '!=',
GT: '>',
LT: '<',
GTE: '>=',
LTE: '<=',
IN: 'IN',
ON: 'ON',
NOT: 'NOT',
LIKE: 'LIKE',
NL: 'NOT LIKE',
NI: 'NOT IN',
NAN: 'IS NULL',
VAL: 'IS NOT NULL',
BT: 'BETWEEN',
}
// 通过sys_data_acl获取数据的权限
function getAclData(dataId, level) {
let userAccess = SQL.query(
'system_sql',
`SELECT 1 FROM sys_data_acl WHERE data_id =? AND level > ? AND delete_time IS NULL;`,
dataId, level - 1
)
return userAccess.length != 0
}

View File

@@ -0,0 +1,107 @@
// ==========================================================================
// 所属模块DevGenTableColumns
// 生成日期2024-02-20 16:41:27 +0800 CST
// 生成路径: apps/system/function.js
// 生成人xScript_Engine
// 数据表dm_table_columns - DevGenTableColumns
// ==========================================================================
function select() {
let value = payload.get().path.split('/').pop();
let columns = "*";
if (payload.get().query?.showAll) {
SQL.cache.remove('system_sql',
`SELECT * FROM dm_table_columns WHERE table_id = '${value}';`)
SQL.cache.remove('system_sql',
`SELECT * FROM dm_tables WHERE table_id = '${value}';`
)
} else {
columns = `column_comment AS columnTitle,dict_type AS dictType,field_name AS fieldName,html_width AS htmlWidth,html_type AS htmlType,is_list AS isList,is_query AS isQuery,sort`;
}
const result = SQL.query('system_sql', `SELECT ${columns} FROM dm_table_columns WHERE delete_time IS NULL AND table_id = ?`, value);
if (result.length) {
const newResult = result.map((itemR) => {
const obj = {};
Object.keys(itemR).forEach((item) => {
obj[underscoreToCamel(item)] = itemR[item];
});
return obj;
});
if (newResult) {
return okMsg(newResult);
}
}
return errMsg(507, '获取失败');
}
function update() {
const data = payload.get().body?.toObject();
delete data.createTime;
delete data.updateTime;
// 验证data.tableName 和 data.columnName 只能有字母数字下划线
if (!/^[a-zA-Z0-9_]+$/.test(data.tableName) || !/^[a-zA-Z0-9_]+$/.test(data.columnName) || !/^[a-zA-Z0-9_]+$/.test(data.dbName)) {
return errMsg(405, '不支持的数据');
}
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
let filter = payload.get().path.split('/').pop();
const columnsArr = Object.keys(newData);
let dataArr = [];
const placeholders = [];
columnsArr.forEach((item) => {
if (newData[item]) {
placeholders.push(`${item} = ?`);
dataArr.push(newData[item]);
// 如果在 item是 is_index 字段,并且值为 1那么将 对应的数据表里面的字段添加到索引里面
if (item == 'is_index' && newData[item] == 1) {
const indexSql = `ALTER TABLE ${data.TABLE_NAME} ADD INDEX idx_x_${data.columnName} (${data.columnName})`;
SQL.push(data.dbName, indexSql);
}
} else {
placeholders.push(`${item} = NULL`);
// 如果在 item是 is_index 字段,并且值为 0那么将 对应的数据表里面的字段删除索引
if (item == 'is_index' && newData[item] == 0) {
if (t.length > 15 || c.length > 15) {
return errMsg(413, '数据过长');
}
const indexSql = `ALTER TABLE ${data.TABLE_NAME} DROP INDEX idx_x_${data.columnName}`;
SQL.push(data.dbName, indexSql);
}
}
});
const newPlaceholders = placeholders.join(',');
const query = `UPDATE dm_table_columns SET ${newPlaceholders} WHERE column_id = ?`;
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return select();
case 'PUT':
return update();
default:
response.status.notFound();
return `404 page not found`;
}
}
// response.headers.add('X-Script-Version', 'system-gentablecolumns-gen-146')
// var tableId = '2'
// TABLE_NAME = 'dm_table_columns'
// COLUMN_KEY = 'table_id'
// 'use api/dm/sql/sqlBaseCurdByTableId.js'

View File

@@ -0,0 +1,35 @@
// 查询数据表结构
function getTableStruct(tableId) {
let validated = validateTableId(tableId)
if (!validated) {
return validated
}
const res = SQL.cache.query('system_sql', 180,
`SELECT * FROM dm_table_columns WHERE table_id = '${tableId}';`)
return res
}
// 查询数据表结构
function getTableMeta(tableId) {
let validated = validateTableId(tableId)
if (!validated) {
return validated
}
const res = SQL.cache.query('system_sql', 180,
`SELECT * FROM dm_tables WHERE table_id = '${tableId}';`
)
if (res.length == 0) {
return false
}
return res[0]
}
function validateTableId(tableId) {
// 过滤字符 tableId 规则为 0-9a-zA-Z
if (/^[0-9a-zA-Z]+$/.test(tableId)) {
return true
}
return false
}

View File

@@ -0,0 +1,20 @@
// ===== 获取dm_tables表所有数据
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const result = SQL.query('system_sql', 'SELECT table_id,table_name,class_name,package_name FROM dm_tables WHERE delete_time IS NULL');
if (result.length) {
const newResult = result.map((itemR) => {
const obj = {};
Object.keys(itemR).forEach((item) => {
obj[underscoreToCamel(item)] = itemR[item];
});
return obj;
});
return okMsg(newResult);
}
return errMsg('获取失败');
}

View File

@@ -0,0 +1,59 @@
// ==========================================================================
// 所属模块DevGenCode
// 生成日期2024-02-20 21:43:27 +0800 CST
// 生成路径: api/dm/template/function.js
// 生成人xScript_Engine
// 数据表dm_tables - DevGenTables
// dm_tables_column - DevGenTablesColumn
// ==========================================================================
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const tableId = payload.get().path.split('/').pop()
let tablesInfo = SQL.query('system_sql', 'SELECT * FROM dm_tables WHERE table_id =?', tableId)
if (!tablesInfo) {
return errMsg(404, '未找到数据')
}
tablesInfo = tablesInfo[0]
let tableInfo = Object.keys(tablesInfo).reduce((acc, key) => {
acc[underscoreToCamel(key.toLowerCase())] = tablesInfo[key];
return acc;
}, {});
let columns = SQL.query('system_sql', 'SELECT * FROM dm_table_columns WHERE table_id =?', tableId)
if (!columns) {
return errMsg(404, '未找到数据')
}
let result = []
columns.forEach(t => {
const newObj = Object.keys(t).reduce((acc, key) => {
acc[underscoreToCamel(key.toLowerCase())] = t[key];
return acc;
}, {});
result.push(newObj)
});
tableInfo.columns = result
// return okMsg(tableInfo)
let et = fs.read('api/dm/template/vue/edit-vue.template').toString()
let lt = fs.read('api/dm/template/vue/list-vue.template').toString()
let ef = ex.template(et, tableInfo).toString()
let lf = ex.template(lt, tableInfo).toString()
let res = {
edit: ef,
list: lf
}
return okMsg(res)
}

View File

@@ -0,0 +1,177 @@
<template>
<div class="system-{{.businessName}}-container">
<el-dialog v-model="state.isShowDialog" width="769px" center>
<template #title>
<div style="font-size: large" v-drag="['.system-{{.businessName}}-container .el-dialog', '.system-{{.businessName}}-container .el-dialog__header']">{{"{{"}}title{{"}}"}}</div>
</template>
<el-form
:model="state.ruleForm"
:rules="state.ruleRules"
ref="ruleFormRef"
label-width="80px"
>
{{- range $index, $column := .columns -}}
{{- if and (eq $column.isInsert 1) (ne $column.isPk 1) (ne $column.fieldName "createTime") (ne $column.fieldName "updateTime") (ne $column.fieldName "deleteTime") -}}
{{- if eq $column.htmlType "input" }}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-input v-model="state.ruleForm.{{$column.fieldName}}" placeholder="请输入{{$column.columnComment}}" />
</el-form-item>
{{- else if eq $column.htmlType "switch" }}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-switch v-model="state.ruleForm.{{$column.htmlField}}" />
</el-form-item>
{{- else if eq $column.htmlType "select" }}
{{if ne $column.dictType ""}}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-select v-model="state.ruleForm.{{$column.fieldName}}" placeholder="请选择{{$column.columnComment}}">
<el-option
v-for="dict in state.{{$column.fieldName}}Options"
:key="dict.dictValue"
:value="Number(dict.dictValue)? Number(dict.dictValue) : dict.dictValue"
:label="dict.dictLabel"
>{{"{{"}} dict.dictLabel {{"}}"}}</el-option>
</el-select>
</el-form-item>
{{ end }}
{{- else if eq $column.htmlType "radio" }}
{{if ne $column.dictType ""}}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-radio-group v-model="state.ruleForm.{{$column.fieldName}}">
<el-radio
v-for="dict in state.{{$column.fieldName}}Options"
:key="dict.dictValue"
:label="dict.dictLabel"
>{{"{{"}} dict.dictLabel {{"}}"}}</el-radio>
</el-radio-group>
</el-form-item>
{{- end }}
{{- else if eq $column.htmlType "datetime" }}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-date-picker clearable style="width: 200px"
v-model="state.ruleForm.{{$column.fieldName}}"
type="date" value-format="YYYY-MM-DD"
placeholder="选择{{$column.columnComment}}">
</el-date-picker>
</el-form-item>
{{- else if eq $column.htmlType "textarea" -}}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-input v-model="state.ruleForm.{{$column.htmlField}}" type="textarea" placeholder="请输入{{$column.columnComment}}" />
</el-form-item>
{{- else if eq $column.htmlType "checkbox" }}
<el-form-item label="{{$column.columnComment}}" prop="{{$column.fieldName}}">
<el-checkbox-group v-model="state.ruleForm.{{$column.fieldName}}">
<el-checkbox
v-for="dict in state.{{$column.fieldName}}Options"
:key="dict.dictValue"
:label="dict.dictLabel"
>{{"{{"}} dict.dictLabel {{"}}"}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
{{- end -}}
{{- end -}}
{{- end }}
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel">取 消</el-button>
<el-button type="primary" @click="onSubmit" :loading="state.loading">编 辑</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="edit{{.functionName}}">
import { reactive, ref, unref, getCurrentInstance } from "vue";
import { get,add, update } from "@/api/public/basic";
import { ElMessage } from "element-plus";
const props = defineProps({
title: {
type: String,
default: () => "",
},
})
const { proxy } = getCurrentInstance() as any;
const ruleFormRef = ref<HTMLElement | null>(null);
const state = reactive({
// 是否显示弹出层
isShowDialog: false,
loading: false,
ruleForm: {
{{- range $index, $column := .columns -}}
{{$column.fieldName}}: undefined,
{{- end}}
},
{{- range $index, $column := .columns -}}
{{- if ne $column.dictType nil }}
// {{$column.fieldName}}Options字典数据
{{$column.fieldName}}Options: [],
{{- end -}}
{{- end }}
// 表单校验
ruleRules: {
{{- range $index, $column := .columns -}}
{{- if and (eq $column.isRequired 1) ($column.columnComment)}}
{{$column.fieldName}}: [
{ required: true, message: "{{$column.columnComment}}不能为空", trigger: "blur" }
],
{{- end}}
{{- end}}
},
});
// 打开弹窗
const openDialog = (row: any) => {
get(row.{{.pkFieldName}}, '{{.tableId}}').then((response) => {
state.ruleForm = response.data?.length ? response.data[0] : {};
});
state.isShowDialog = true;
state.loading = false;
{{- range $index, $column := .columns -}}
{{- if ne $column.dictType nil }}
proxy.getDicts("{{$column.dictType}}").then((response: any) => {
state.{{$column.fieldName}}Options = response.data;
});
{{- end -}}
{{- end}}
}
// 关闭弹窗
const closeDialog = (row?: object) => {
proxy.mittBus.emit("onEdit{{.functionName}}Module", row);
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 保存
const onSubmit = () => {
const formWrap = unref(ruleFormRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
if (state.ruleForm?.flowId) {
update(state.ruleForm.{{.pkFieldName}}, state.ruleForm, '{{.tableId}}').then((response) => {
ElMessage.success('修改成功');
state.loading = false;
closeDialog(state.ruleForm); // 关闭弹窗
});
} else {
add(state.ruleForm, '{{.tableId}}').then((response) => {
ElMessage.success('新增成功');
closeDialog(state.ruleForm); // 关闭弹窗
});
}
}
});
};
defineExpose({
openDialog,
});
</script>

View File

@@ -0,0 +1,340 @@
<template>
<el-card style="margin-bottom: 20px">
<!-- 查询 -->
<el-form :model="state.queryParams" ref="queryForm" :inline="true">
<el-form-item v-for="(item, index) in filteredQueryData" :label="item.columnTitle" :prop="item.fieldName"
:key="index">
<el-select v-if="item.htmlType == 'select'" v-model="state.queryParams[item.fieldName]"
placeholder="请选择" clearable style="width: 240px">
<el-option v-for="dict in state[item.fieldName + 'Option']" :key="dict.dictValue"
:label="dict.dictLabel" :value="dict.dictValue" />
</el-select>
<el-date-picker v-else-if="item.htmlType == 'datetime'" v-model="state.queryParams[item.fieldName]"
type="date" placeholder="请选择" clearable style="width: 240px" />
<el-input v-else v-model="state.queryParams[item.fieldName]" :placeholder="'请输入'" clearable
@keyup.enter.native="handleQuery()" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery()">
<SvgIcon name="elementSearch" />搜索
</el-button>
<el-button @click="resetQuery">
<SvgIcon name="elementRefresh" />
重置
</el-button>
</el-form-item>
</el-form>
</el-card>
<div v-loading="state.loading" style="min-height: 600px">
<el-card class="box-card" v-if="filteredListData.length">
<template #header>
<div class="card-header">
<span class="card-header-text">{{.tableComment}}</span>
<div>
<el-button type="primary" plain v-auth="`{{.className}}:add`" @click="onOpenAddModule">
<SvgIcon name="elementPlus" />新增
</el-button>
<el-button type="success" plain @click="onExport">
<SvgIcon name="elementDownload" />导出
</el-button>
<el-button type="danger" plain v-auth="`{{.className}}:delete`" :disabled="state.multiple"
@click="onTableRowDel">
<SvgIcon name="elementDelete" />删除
</el-button>
</div>
</div>
</template>
<!--数据表格-->
<el-table :data="state.tableData" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-for="(item, index) in filteredListData" :key="index" :label="item.columnTitle"
:width="item.htmlWidth ? item.htmlWidth : ''" :prop="item.fieldName">
<template #default="scope" v-if="$slots[item.fieldName]">
<slot :name="item.fieldName" :row="scope.row"></slot>
</template>
<template #default="scope" v-if="item.dictType">
<el-tag type="success" disable-transitions>{{ "{{" }} findDictLabel(scope.row[item.fieldName],
item.fieldName) || '-- --' {{ "}}" }}</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{ { dateStrFormat(scope.row.createTime) } }</span>
</template>
</el-table-column> -->
<el-table-column label="操作" align="center" v-auth="`{{.className}}:delete` && `{{.className}}:edit`"class-name="small-padding fixed-width">
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center; gap: 10px">
<div>
<el-tooltip class="box-item" effect="dark" content="编辑" placement="top-start">
<el-button type="primary" size="small" circlesize="small" circle
v-auth="`{{.className}}:edit`" @click="onOpenEditModule(scope.row)">
<SvgIcon :size="12" name="elementEdit" />
</el-button>
</el-tooltip>
</div>
<div>
<slot name="listBtn" :row="scope.row"></slot>
</div>
<div>
<el-tooltip class="box-item" effect="dark" content="删除" placement="top-start">
<el-button type="danger" size="small" circle v-auth="`{{.className}}:delete`"
@click="onTableRowDel(scope.row)">
<SvgIcon :size="12" name="elementDelete" />
</el-button>
</el-tooltip>
</div>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页设置-->
<div v-show="state.total > 0">
<el-divider></el-divider>
<el-pagination background :total="state.total" :current-page="state.queryParams.pageNum"
:page-size="state.queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</el-card>
</div>
<EditModule ref="editModuleRef" />
</template>
<script lang="ts" setup name="Partner">
import { reactive, ref, onMounted, getCurrentInstance, onUnmounted, computed, watch } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditModule from './component/editModule.vue';
import { del, get, getStruct } from '@/api/public/basic';
import { useRoute } from 'vue-router';
const { proxy } = getCurrentInstance() as any;
const editModuleRef = ref();
const route = useRoute();
const state = reactive({
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 表格数据
tableData: [],
// 总条数
total: 0,
// 表格列数据
tableStructData: [],
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
loading: false,
// 查询参数
queryParams: {
// 页码
pageNum: 1,
// 每页大小
pageSize: 10,
},
});
// 可搜索的字段
const filteredQueryData = computed(() => {
return state.tableStructData.filter((item: any) => item.isQuery) || [];
});
// 列表展示字段
const filteredListData = computed(() => {
return state.tableStructData.filter((item: any) => item.isList) || [];
});
/** 查询列表 */
const handleQuery = () => {
const params = state.queryParams;
state.loading = true;
// 如果state.queryParams中有空值则删除
Object.keys(params).forEach((key) => {
if (!params[key]) {
delete params[key];
}
});
get(params, '{{.tableId}}').then((response: any) => {
state.tableData = response.data.result;
state.total = response.data.count;
state.loading = false;
});
};
onMounted(() => {
// 获取data struct
getStruct('{{.tableId}}').then((res: any) => {
if (res.status == 0) {
const arr = res.data.map((item: any) => {
if (item.isQuery) {
state.queryParams[item.fieldName] = undefined;
}
// 如果存在字典数据
if ((item.isQuery || item.isList) && item.dictType) {
proxy.getDicts(item.dictType).then((response: any) => {
state[item.fieldName + 'Option'] = response.data;
});
}
return item;
});
arr.sort(compareSorts);
state.tableStructData = arr;
handleQuery();
}
});
})
/** 字典label */
const findDictLabel = (dictValue, fieldName) => {
if (!state[fieldName + 'Option']) return;
const obj = state[fieldName + 'Option'].find((item) => item.dictValue == dictValue) || {};
return obj.dictLabel;
};
/**比较两个对象的 sort 属性*/
const compareSorts = (a: any, b: any) => {
// 如果 sort 属性为 null则将其视为无穷大
if (a.sort === null || b.sort === null) {
return 0; // 不参与排序
}
// 比较 sort 属性
return a.sort - b.sort;
};
/** 重置按钮操作 */
const resetQuery = () => {
state.tableStructData.forEach((item: any) => {
if (item.isQuery) {
state.queryParams[item.columnName] = undefined;
}
return item;
});
handleQuery();
};
const handleCurrentChange = (val: number) => {
state.queryParams.pageNum = val;
handleQuery();
};
const handleSizeChange = (val: number) => {
state.queryParams.pageSize = val;
handleQuery();
};
// 打开新增配置参数弹窗
const onOpenAddModule = () => {
state.title = '创建公共文件';
editModuleRef.value.openDialog({});
};
// 打开编辑配置参数弹窗
const onOpenEditModule = (row: object) => {
state.title = '修改文件名';
editModuleRef.value.openDialog(row);
};
/** 删除按钮操作 */
const onTableRowDel = (row: any) => {
const ids = row['{{.pkFieldName}}'] ? [row['{{.pkFieldName}}']] : state.ids;
ElMessageBox({
message: '是否确认删除的数据项?',
title: '警告',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(function () {
return del({ ['{{.pkFieldName}}']: ids }, '{{.tableId}}').then(() => {
handleQuery();
ElMessage.success('删除成功');
});
});
};
// 多选框选中数据
const handleSelectionChange = (selection: any) => {
state.ids = selection.map((item: any) => {
return item['{{.pkFieldName}}'];
});
state.single = selection.length != 1;
state.multiple = !selection.length;
};
// 页面加载时
onMounted(() => { });
// 页面卸载时
onUnmounted(() => {
proxy.mittBus.off('onEditModule');
});
// 导出功能
const onExport = async () => {
// 判断是否有勾选项
let list = [];
if (state.ids && state.ids.length > 0) {
// 有勾选项,仅导出勾选数据
list = state.tableData.filter((row) => state.ids.includes(row.flowId));
if (!list.length) {
ElMessage.warning('无可导出数据');
return;
}
} else {
// 无勾选项,导出全部数据
const params = { ...state.queryParams };
Object.keys(params).forEach((key) => {
if (!params[key]) {
delete params[key];
}
});
// 先获取总数
const countRes = await get(params, 'hS9i1PUcWXs');
const total = countRes.data.count;
if (!total) {
ElMessage.warning('无可导出数据');
return;
}
// 拉取所有数据
const allParams = { ...params, pageNum: 1, pageSize: total };
const allRes = await get(allParams, 'hS9i1PUcWXs');
list = allRes.data.result || [];
}
// 处理表头和字段
const columns = filteredListData.value;
const header = columns.map((col) => col.columnTitle);
const fields = columns.map((col) => col.fieldName);
// 处理字典类型显示
const rows = list.map((row) => {
return fields.map((field, idx) => {
const col = columns[idx];
if (col.dictType) {
return findDictLabel(row[field], field) || '';
}
return row[field] !== undefined ? row[field] : '';
});
});
// 生成CSV内容
let csvContent = '\uFEFF' + header.join(',') + '\n';
rows.forEach((row) => {
csvContent += row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',') + '\n';
});
// 下载CSV
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = "{{.tableComment}}" + " - " + new Date().toLocaleString().replace(/\//g, '-') + ' - 导出数据' + '.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
</script>

View File

@@ -0,0 +1,19 @@
// ==========================================================================
// 所属模块event - broadcast
// 生成日期2024/10/16
// 生成路径: /api/event/broadcast/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'broadcast-24.10.20')
function main() {
let event = payload.get().query.event
let data = payload.get().body?.toString()
if (sse.broadcast(event, data)) {
return okMsg(true)
} else {
return errMsg(408, false)
}
}

View File

@@ -0,0 +1,39 @@
// ==========================================================================
// 所属模块event - heartbeat
// 生成日期2024/10/16
// 生成路径: /api/event/heartbeat/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'heartbeat-24.10.20')
function main() {
// 取 Authorization - Bearer 后面的Token值
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookie ? payload.get().cookie["token"] : null);
if (!userToken) {
return errMsg(413, "未登录");
}
// 判断userToken是否是数组
if (Array.isArray(userToken)) {
userToken = userToken[0]
}
// 删除 Bearer 部分
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
if (!tokenInfo) {
return errMsg(413, "身份验证失败");
}
userToken = crypto.hash.md5(userToken).toHEX.encoding().toString()
let sessions = cache.get('session', tokenInfo.uid)
if (sessions) {
if (!sessions.includes(userToken)) {
sessions.push(userToken)
}
} else {
sessions = [userToken]
}
cache.set('session', tokenInfo.uid, sessions, 30 * 60 * 1000)
return okMsg(true);
}

View File

@@ -0,0 +1,117 @@
let test_data = {
"info": {
"id": "202509231006",
"name": "铁力路站~友谊路站测试巡查任务",
"time": "2025-09-23 10:06:57",
"timestamp": 1758593217.160917,
"type": 0
},
"line": {
"code": "03",
"interval": "铁力路~友谊路",
"interval_code": "03001",
"name": "地铁3号线",
"status": 1
},
"risk": {
"content": "发现1个吊车",
"id": "20250923100600003",
"level": 2,
"mark": {
"left": [
1062,
1102
],
"right": [
815,
926
]
},
"photo": {
"origin": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/202509231006/S202509231006-1.jpg",
"result": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/202509231006/R202509231006-1.jpg"
},
"time": "2025-09-23 10:07:19",
"timestamp": 1758593239.038757,
"type": 1011,
"video": {
"origin": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/S202509231006.mp4",
"result": "https://subway-guiyang.oss-cn-shanghai.aliyuncs.com/Chengjian/R202509231006.mp4"
},
"wgs84": {
"x": 31.40948546,
"y": 121.459669216064
}
}
}
// 目标类别
// {
// "Crane": "吊车",
// "Digger": "挖掘机",
// "Mound": "堆土",
// "Steel": "施工钢材",
// "TowerCrane": "塔吊",
// "Bulldozer": "推土车",
// "PileFoundation": "打桩机",
// "Tankcar": "罐车",
// "Grouting": "灌浆车",
// "Roller": "压路车",
// "Construction": "疑似施工机具",
// "Gantry": "龙门架",
// "Ladder": "登高车"
// }
function main() {
let data = payload.get()?.body?.toObject()
// 判断data的数据结构是否和test_data匹配
if (data && data.info && data.risk && data.line) {
const { name, time: findTime,id:taskId } = data.info || {}
const { interval_code:stationId } = data.line || {}
const { wgs84, photo,level,type } = data.risk || {}
const extra = JSON.stringify(data)
if (name && findTime && wgs84 && wgs84.lng && wgs84.lat && taskId && stationId) {
const id = ex.suid().base58()
let fileId
if (photo?.origin) {
const img = photo.result
// 存储图片
fileId = ex.suid().base58()
const fileRes = SQL.exec('system_sql', `INSERT INTO sys_files (file_id, file_path,file_name) VALUES (?, ?,?)`, fileId, img, '标注图片.png')
if (!fileRes) {
return errMsg(500, '图片存储失败')
}
}
// 插入事件表
const res = SQL.exec('system_sql', `INSERT INTO dr_event (name, find_time, longitude, latitude,extra,proprietor_id,
proprietor_name,
station_id,
task_id,
id
${fileId ? ',file_img' : ''}
${level||level==0 ? ',abnormal_level' : ''}
${type ? ',aim_type' : ''}
) VALUES (?, ?, ?, ?,?,?,?,?,?,? ${fileId ? ',?' : ''} ${level||level==0 ? ',?' : ''} ${type ? ',?' : ''})`,
name, findTime, wgs84.lng, wgs84.lat, extra, '1', '系统推送',
stationId, taskId,
id,
fileId ? fileId : null,
level||level==0 ? level : null,
type ? type : null
)
if (res) {
return okMsg(true)
}
}
// 记录缺失字段
const miss = []
if (!name) miss.push('name')
if (!findTime) miss.push('time')
if (!wgs84) miss.push('wgs84')
if (!wgs84.lat) miss.push('wgs84.lat')
if (!wgs84.lng) miss.push('wgs84.lng')
if (!taskId) miss.push('taskId')
if (!stationId) miss.push('stationId')
return errMsg(400, `缺少字段: ${miss.join(', ')}`)
}
return errMsg(400, "数据结构错误")
}

View File

@@ -0,0 +1,20 @@
// ==========================================================================
// 所属模块event - push
// 生成日期2024/10/16
// 生成路径: /api/event/push/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'pusher-24.10.20')
function main() {
let sid = payload.get().path.split('/').pop()
let event = payload.get().query.event
let data = payload.get().body?.toString()
if (sse.send(sid, event, data)) {
return okMsg(true)
} else {
return errMsg(408, false)
}
}

View File

@@ -0,0 +1,26 @@
// ==========================================================================
// 所属模块event - user
// 生成日期2024/10/16
// 生成路径: /api/event/user/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'user-24.10.20')
function main() {
let uid = payload.get().path.split('/').pop()
let event = payload.get().query.event
let data = payload.get().body?.toString()
let sessions = cache.get('session', uid)
for (let i in sessions) {
if (!sse.send(sessions[i], event, data)) {
// sse.send 失败,可能是 session 过期,删除该 session
delete sessions[i]
cache.set('session', uid, sessions)
}
}
return okMsg(true)
}

36
server/api/f/function.js Normal file
View File

@@ -0,0 +1,36 @@
// ==========================================================================
// 所属模块fn
// 生成日期2024/10/16
// 生成路径: /api/x/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'fn-24.10.20')
function main() {
// APPID认证
'use api/lib/userPassport.js'
// runtime.exec('api/x/hook.js', ...args)
let fnId = payload.get().query.f
// let app = SQL.cache.query(DB_NAME, 1800, 'SELECT * FROM fn_apps WHERE fn_id =?', fnId)
let app = SQL.query(DB_NAME, 'SELECT * FROM fn_apps WHERE delete_time IS NULL AND status =1 AND (fn_id =? OR alias =?);', fnId, fnId)
if (!app) {
return errMsg(500, "逻辑函数不存在")
}
app = app[0]
TABLE_INFO.cache_life == app?.cache_life || 0
if (app?.cache_life == 0) {
// 声明缓存
payload.set.header("Cache-Control", ['skip-cache'])
response.headers.set(
'Cache-Control',
'no-transform,no-cache,immutable'
)
}
return runtime.call(app.code)
}

View File

@@ -0,0 +1,37 @@
// ==========================================================================
// 所属模块fn
// 生成日期2024/10/16
// 生成路径: /api/x/function.js
// 生成人xScript_Engine
// 数据表system
// ==========================================================================
response.headers.add('X-Script-Version', 'fn-24.10.20')
function main() {
// APPID认证
'use api/lib/userPassport.js'
// runtime.exec('api/x/hook.js', ...args)
let fnId = payload.get().query.f
// let app = SQL.cache.query(DB_NAME, 1800, 'SELECT * FROM fn_apps WHERE fn_id =?', fnId)
let app = SQL.query(DB_NAME, 'SELECT * FROM fn_apps WHERE delete_time IS NULL AND (fn_id =? OR alias =?);', fnId, fnId)
if (!app) {
return errMsg(500, "逻辑函数不存在")
}
DB_NAME = 'develop_sql'
app = app[0]
if (!app.cache_life || app.cache_life == 0) {
// 声明缓存
payload.set.header("Cache-Control", ['skip-cache'])
response.headers.set(
'Cache-Control',
'no-transform,no-cache,immutable'
)
}
return runtime.call(`${app.code};main()`)
}

View File

@@ -0,0 +1,63 @@
// ==========================================================================
// 所属模块files
// 生成日期2024-02-08 17:09:21 +0800 CST
// 生成路径: files/function.js
// 生成人xScript_Engine
// 数据表sys_files - 文件管理
// ==========================================================================
response.headers.add('X-Script-Version', 'uploads-1.0');
// DB_NAME = 'system_sql'
function add() {
const params = payload.get().body?.toObject();
if (params.isCatalogue) {
// 目录
const sql = `INSERT INTO sys_files (file_id,file_name,privacy,is_folder,parent_id, proprietor_id, proprietor_name) VALUES (?, ?, ?, ?, ?, ?, ?);`;
const res = SQL.exec('system_sql', sql, ex.suid().base58(), params.name, params.isPublic, params.isCatalogue, params.parentId, USER_INFO.user_id, USER_INFO.nickname);
return res;
}
}
function list() {
const params = payload.get().query;
if (params && params.isPublic == 0) {
// 非公共文件
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND privacy = 0 AND proprietor_id = ?`, params.userId);
return JSON.stringify(res);
}
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND (privacy != 0 OR privacy IS NULL)`);
return JSON.stringify(res);
}
function update() {
const params = payload.get().body?.toObject();
const res = SQL.exec('system_sql', `UPDATE sys_files SET file_name = ? WHERE file_id = ?`, params.name, params.id);
return res;
}
function del() {
const fileId = payload.get().query.fileId;
const res = SQL.exec('system_sql', `UPDATE sys_files SET delete_time = NOW() WHERE file_id = ?`, fileId);
return res;
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list();
case 'POST':
return add();
case 'PUT':
return update();
case 'DELETE':
return del();
default:
response.status.notFound();
return `404 page not found`;
}
}

106
server/api/fn/function.js Normal file
View File

@@ -0,0 +1,106 @@
/** 页面管理基础接口 **/
TABLE_NAME = 'fn_apps';
COLUMN_KEY = 'fn_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js';
switch (payload.get().method) {
case "GET":
return list();
case "POST":
return insert();
case "PUT":
return sql_update();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'name') {
placeholders.push(`name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function list() {
const queryPa = payload.get().query;
if (!Object.keys(queryPa).length) {
// 根据id查询详情
var useParent = scriptPath != payload.get().path.substring(1);
if (useParent) {
let value = payload.get().path.split('/').pop();
const result = SQL.query(
'system_sql',
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
value
);
if (result.length) {
return okMsg(result);
}
return errMsg('获取失败');
}
}
// 格式化这个query参数
let newData = {};
Object.keys(queryPa).forEach((item) => {
newData[item] = queryPa[item][0];
});
// 总数据
const COUNT_TOTAL = countTotal(newData);
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
return okMsg({ result, count: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
}
function insert() {
const data = payload.get().body?.toObject();
const newData = { ...data };
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = [];
columnsArr.push(COLUMN_KEY)
columnsArr.push('proprietor_id')
columnsArr.push('proprietor_name')
const suid = ex.suid().base58()
dataArr.push(suid)
dataArr.push(USER_INFO.user_id)
dataArr.push(USER_INFO.nickname)
dataArr.forEach(() => {
placeholders.push('?');
});
placeholders.join(',');
const columnsStr = columnsArr.join(',');
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec('system_sql', query, ...dataArr);
if (res) {
return okMsg(suid);
}
return errMsg(507, '添加失败');
}

View File

@@ -0,0 +1,21 @@
// 将驼峰命名改为下划线
function camelToUnderscore(str) {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}
// 将下划线改为驼峰命名
function underscoreToCamel(str) {
return str.replace(/_([a-z])/g, function (match, letter) {
return letter.toUpperCase();
});
}
// 将首字母转换为大写
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// 将首字母转换为小写
function lowercaseFirstLetter(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
}

View File

@@ -0,0 +1,11 @@
// ==========================================================================
// 所属模块lib - debugSetting
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/debugSetting.js
// 生成人Cato
// 功能描述:用于定义调试模式的一些配置策略。
// ==========================================================================
// console.warn('@ CURRENT SERVICE RUNNING DEBUG MODE @')
// runtime.cache.purge()
runtime.cache.disable()

View File

@@ -0,0 +1,262 @@
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
class GormCommonClass {
constructor(props) {
this.wheres = [];
this.values = [];
this.tabName = props.tabName;
}
getWhereStr() {
if (this.wheres.length) {
return ` WHERE ${this.wheres.join(" AND ")}`;
}
return "";
}
where(w, ...vals) {
this.wheres.push(w);
this.values.push(...vals);
return this;
}
verifyLen(sql, vals) {
const wLen = sql.split("?")?.length - 1;
if (wLen != vals.length) {
throw "占位符与值的数量不匹配";
}
}
}
class GormSelectClass extends GormCommonClass {
#selectKeys = [];
#tabName = "";
#offset;
#limit = 1;
#groupBys = [];
#orderBys = [];
constructor(props) {
super(props);
this.#selectKeys = props.selectKeys;
this.#tabName = props.tabName;
}
where(w, ...vals) {
this.wheres.push(w);
this.values.push(...vals);
return this;
}
offset(val) {
this.#offset = Number(val) || 0;
return this;
}
limit(val) {
this.#limit = Number(val) || 1;
return this;
}
orderBy(orderKey, sort) {
this.#orderBys.push(`${orderKey} ${sort}`);
return this;
}
groupBy(...agrs) {
this.#groupBys.push(...agrs);
return this;
}
build() {
let sqlStr = `SELECT ${this.#selectKeys.join(",")} FROM ${this.#tabName}`;
sqlStr += this.getWhereStr();
if (this.#groupBys.length) {
sqlStr += " GROUP BY ";
sqlStr += this.#groupBys.join(", ");
}
if (this.#orderBys.length) {
sqlStr += " ORDER BY ";
sqlStr += this.#orderBys.join(", ");
}
if (this.#limit) {
sqlStr += ` LIMIT ${this.#limit}`;
}
if (this.#offset) {
sqlStr += ` OFFSET ${this.#offset}`;
}
this.verifyLen(sqlStr, this.values);
return {
sql: sqlStr,
values: this.values,
};
}
}
class GormUpdateClass extends GormCommonClass {
#tabName = "";
#updateList = [];
constructor(props) {
super(props);
this.#tabName = props.tabName;
this.#init(props.updateData || {});
}
#init(updateData) {
const updateKeys = Object.keys(updateData);
if (!updateKeys.length) {
throw "没有需要修改的key";
}
for (const key of updateKeys) {
this.#updateList.push(`${key} = ?`);
let data = updateData[key];
if (typeof data == "object" && data != null && !(data instanceof Date)) {
data = JSON.stringify(data);
}
if (data instanceof Date) {
data = formatDate(data);
}
this.values.push(data || null);
}
}
where(w, ...vals) {
this.wheres.push(w);
this.values.push(...vals);
return this;
}
build() {
let sqlStr = `UPDATE ${this.#tabName}`;
if (!this.#updateList) {
throw "没有需要修改的key";
}
if (!this.wheres.length) {
throw "修改数据必须指定条件";
}
sqlStr += " SET ";
sqlStr += this.#updateList.join(", ");
sqlStr += this.getWhereStr();
this.verifyLen(sqlStr, this.values);
return {
sql: sqlStr,
values: this.values,
};
}
}
class GormInsertClass {
#insertKeys = [];
#tabName = "";
#values = [];
constructor(props) {
this.#tabName = props.tabName;
this.#init(props.insertData);
}
#init(insertData) {
const insertKeys = Object.keys(insertData);
if (!insertKeys.length) {
throw "没有需要插入的key";
}
this.#insertKeys = insertKeys;
for (const key of insertKeys) {
let data = insertData[key];
if (typeof data == "object" && data != null && !(data instanceof Date)) {
data = JSON.stringify(data);
}
if (data instanceof Date) {
data = formatDate(data);
}
this.#values.push(data || null);
}
}
verifyLen(sql, vals) {
const wLen = sql.split("?")?.length - 1;
if (wLen != vals.length) {
throw "占位符与值的数量不匹配";
}
}
build() {
let sqlStr = `INSERT INTO ${this.#tabName} (${this.#insertKeys.join(
", "
)})`;
sqlStr += ` VALUES (${this.#insertKeys.map(() => "?").join(", ")})`;
this.verifyLen(sqlStr, this.#values);
return {
sql: sqlStr,
values: this.#values,
};
}
}
class GormClass {
#props;
constructor(props) {
this.#props = props;
}
select(...selectKeys) {
return new GormSelectClass({ ...this.#props, selectKeys });
}
update(updateData) {
return new GormUpdateClass({ ...this.#props, updateData });
}
delete() {
return new GormUpdateClass({
...this.#props,
updateData: { delete_time: new Date() },
});
}
insert(insertData) {
return new GormInsertClass({ ...this.#props, insertData });
}
}

View File

@@ -0,0 +1,55 @@
function getReqData() {
const req = payload.get();
const data = req.body?.toString() ? req.body?.toObject() : {};
return { data, query: req.query };
}
function getUniqueId() {
return ex.suid().base58();
}
function loginVerify() {
const req = payload.get();
const token = req?.cookie?.token;
if (!token) {
return;
}
return jwt_parse(token);
}
function main() {
const { query } = getReqData();
let [action] = query.action || [""];
const userInfo = loginVerify() || {};
if (!userInfo && !actionWhiteList.includes(action)) {
return errMsg(413, "Invalid or missing token");
}
return runAction(userInfo);
}
function runAction(userInfo) {
const { query } = getReqData();
let [action] = query.action || [""];
if (!action) {
return errMsg(500, "没有action字段,请检查");
}
const apiMethod = payload.get().method;
const fnData = actionControls.find((f) => {
const { run, method } = f;
return run.name == action && method == apiMethod;
});
if (fnData) {
return fnData.run(userInfo);
}
return errMsg(500, "Invalid or missing action Or method");
}

View File

@@ -0,0 +1,61 @@
/**
* 获取文件的 MIME 类型
* @param {string} filePath - 文件路径
* @returns {string} - 文件的 MIME 类型
*/
function getFileMimeType(filePath) {
const extension = filePath.split('.').pop().toLowerCase();
switch (extension) {
case 'htm':
case 'html':
return 'text/html';
case 'css':
return 'text/css';
case 'js':
return 'text/javascript';
case 'json':
return 'application/json';
case 'xml':
return 'application/xml';
case 'png':
return 'image/png';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'gif':
return 'image/gif';
case 'svg':
return 'image/svg+xml';
case 'ico':
return 'image/x-icon';
case 'pdf':
return 'application/pdf';
case 'doc':
case 'docx':
return 'application/msword';
case 'xls':
case 'xlsx':
return 'application/vnd.ms-excel';
case 'ppt':
case 'pptx':
return 'application/vnd.ms-powerpoint';
case 'zip':
return 'application/zip';
case 'rar':
return 'application/rar';
case 'gz':
return 'application/gzip';
case 'mp3':
return 'audio/mpeg';
case 'mp4':
return 'video/mp4';
case 'avi':
return 'video/x-msvideo';
case 'mov':
return 'video/quicktime';
case 'txt':
return 'text/plain';
default:
return 'application/octet-stream';
}
}

16
server/api/lib/package.js Normal file
View File

@@ -0,0 +1,16 @@
// ==========================================================================
// 所属模块lib - package
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/package.js
// 生成人Cato
// 功能描述用于定义全局公共的引用。注册于global.js内)
// ==========================================================================
var fs = require('fs');
var SQL = require('sql');
var ex = require('util');
var cache = require('cache');
var crypto = require('crypto');
const request = require('request');
var encoding = require('encoding');
// var jwt = require('api/user/oauth/jwt/jwt.js');

61
server/api/lib/protect.js Normal file
View File

@@ -0,0 +1,61 @@
// ==========================================================================
// 所属模块lib - protect
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/protect.js
// 生成人Cato
// 功能描述:用于需要保护的脚本文件及数据内容的加解密。
// ==========================================================================
// 内部数据加密
function encryptData(inputData) {
// crypto.rc4(inputData,'/crypto.encrypt/').toBase58.encoding().toString();
return crypto.rc4('/crypto.encrypt/',inputData).toBase58.encoding().toString()
}
// 内部数据解密
function decryptData(inputData) {
// crypto.rc4(inputData,'/crypto.encrypt/').toBase58.decoding().toString();
let data = encoding.from(inputData).toBase58.decoding().toString()
return crypto.rc4('/crypto.encrypt/',data).toString()
}
// var scriptData = global.ps + '\n' + global.js + '\n' + decode(function.ps) + '\n' + function.js
// var prodPath = scriptPath.replace('zcx.the.bb', 'prod.the.bb')
// 写出加密后的js到正式
// const scriptContext = fs.read(prodPath.replace('prod.the.bb', 'zcx.the.bb'))
// if (scriptContext.toString().substring(0, 11) != '@Protected-') {
// const ps = crypto.script.get(scriptContext.toBytes, payload.get().headers.Signet)
// fs.write(ps.toString(), prodPath)
// }
// ps = fs.read('/www/wwwroot/prod.the.bb/function.js')
// ps = crypto.script.get(ps)
// console.log('cryptOut', ps)
// 写出加密后的bin到正式
// crypto.script.encode(
// `${scriptPath}\\index.html`, // 执行脚本
// `${prodPath}\\data.bin` // 加密路径
// )
// 读取加密后的bin文件并decode得到html的string然后通过replace填充字符串完成整个html内容然后通过return返回给前端
// var html = crypto.script.decode(
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\data.bin',
// 'crypto_test' // 解密密码
// )
// var html = '<h2>XAFWD1231</h2>'
// html.replace('XAFWD1231', 'TEST')
// return html
// <h2>TEST</h2>
// crypto.script.decode(
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\function1.js',
// 'C:\\PROJECT\\xkh-h5\\api\\user\\add\\function2.js', // 解密路径
// 'crypto_test' // 解密密码
// )
// console.log(scriptPath)

View File

@@ -0,0 +1,27 @@
// ==========================================================================
// 所属模块lib - requestLog
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/requestLog.js
// 生成人Cato
// 功能描述:用于输出请求日志。
// ==========================================================================
// 日志输出格式:
// 请求地址 > 方式 > 目标地址:端口/路径?参数=值 > 请求体 > 脚本路径
// 6.0.0.14 > GET > 6.0.0.74:7788/extension/staff?pageNum=1&pageSize=10&list=true | > /www/wwwroot/xp-back/extension/staff
if (scriptPath != rootPath + '/core.js' && !scriptPath.includes('uploads')) {
// prodPath += "/function.js"
// if (scriptPath.includes('open')){console.log(payload.toCURL())}
console.log(
// 北京时间 = UTC 时间 + 8 小时
new Date(new Date().getTime() + 8 * 60 * 60 * 1000).toISOString().replace(/T/, ' ').replace(/\..+/, ''),
' * ',
payload.get().remote_addr.split(':')[0],
'>',
payload.get().method,
'>',
payload.get().host + payload.get().url,
// payload.get().body.toString() ? '| ' + payload.get().body.toString() : '',
);
}

View File

@@ -0,0 +1,8 @@
// 格式化单数据参数
function formatSingleParams() {
var params = {}
Object.keys(payload.get().query).forEach(key => {
params[key] = payload.get().query[key][0]
})
return params
}

View File

@@ -0,0 +1,104 @@
// ==========================================================================
// 所属模块lib - responseWriter
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/responseWriter.js
// 生成人Cato
// 功能描述用于全局的请求返回消息结构体的构建。注册于global.js内)
// ==========================================================================
const compress = require('compress')
// 构建成功返回
function okMsg(data) {
let res = {
"status": 0,
"msg": 'success',
"data": data
}
log(1, JSON.stringify(res))
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookies ? payload.get().cookie["token"] : null);
var signature = `${payload.get().method}:${payload.get().host}:${payload.get().url}::${payload.get().url}:${userToken}:${payload.get().body ? payload.get().body.toString() : '>'}`
signature = crypto.hash.md5(signature).toHEX.encoding().toString()
// 检查缓存
if (!TABLE_INFO.cache_life) {
TABLE_INFO.cache_life = 5
}
// console.log(JSON.stringify(APP_INFO));
if (APP_INFO) {
switch (APP_INFO.status) {
case 3:
res = crypto.sm4.encrypt('CBC', APP_INFO.secret.split('').reverse().join(''), JSON.stringify(res), APP_INFO.secret).toBase64.url.encoding().toString()
case 2:
// console.log(APP_INFO.secret)
// console.log(APP_INFO.secret.split('').reverse().join(''))
res.ciphertext = crypto.sm4.encrypt('CBC', APP_INFO.secret.split('').reverse().join(''), JSON.stringify(res), APP_INFO.secret).toBase64.url.encoding().toString()
}
}
// 如果请求头中包含 Accept-Encoding 并且 Accept-Encoding 的值包含 br则设置 Content-Encoding 为 br。
if (payload.get().headers['Accept-Encoding'] && payload.get().headers['Accept-Encoding'][0].includes('br')) {
response.headers.set("Content-Encoding", 'br')
res = compress.brotli.compress(res).toString()
'use api/lib/setResponseCache.js'
response.write(res)
return res
}
res = JSON.stringify(res)
'use api/lib/setResponseCache.js'
response.write(res)
return res
}
// 构建错误返回
function errMsg(code, msg) {
response.status.code(code)
let res = JSON.stringify({
"status": code,
"msg": msg,
})
log(0, res)
response.write(res)
return res
}
// 记录数据流
function log(status, output) {
let time = new Date().getTime()
let dbTime = payload.get().headers["Databus-Time"]
if (dbTime) {
response.headers.add("Server-Timing", `DataBus;dur=${time - dbTime}`)
}
let startTime = payload.get().headers["Timestamp"]
let timeDiff
if (startTime) {
timeDiff = time - startTime
response.headers.add("Server-Timing", `RequestCost;dur=${timeDiff}`)
}
// 记录请求体
payload.get().body = payload.get().body?.toString()
// 记录登录日志
SQL.push('system_sql', 'INSERT INTO log_dataflow (event_id, table_id, path, status, ipaddr, input, sql_stmts, output, user_info, blank) VALUES (?,?,?,?,?,?,?,?,?,?)',
response.headers.get('X-Request-Id') || ex.suid().base58(),
payload.get().query?.t,
payload.get().path,
status,
payload.get().remote_addr.split(':')[0],
JSON.stringify(payload.get()),
SQL_STMTS.length && SQL_STMTS.length > 0 ? JSON.stringify(SQL_STMTS) : null,
output,
USER_INFO ? JSON.stringify(USER_INFO) : null,
timeDiff
);
}

View File

@@ -0,0 +1,29 @@
// 允许接口被公共访问
var ALLOW_PUBLIC_ACCESS
// 获取当前用户的APPID信息
var APP_INFO
// 将ACL验证后的用户信息存储在全局变量中
var USER_INFO
var OPERATION_TITLE
var OPERATION_TYPE
// 例如OPERATION_TITLE = '新增用户'
// 当该请求操作需要被记录到操作日志时,请给该变量赋值。
// 声明数据库查询变量
var TABLE_STRUCT = []
var TABLE_INFO = {}
var TABLE_NAME
var DB_NAME = 'system_sql' // 默认数据库,默认为 system_sql
var COLUMN_KEY
var TABLE_FIELDS
var SQL_STMTS = []
// 从系统配置表中读取配置信息,并存入缓存池中
var SYSTEM_CONFIG = SQL.cache.query(
'system_sql',
86400, // 缓存时间单位秒0表示不缓存-1表示永久缓存默认缓存时间为1天
"SELECT * FROM `sys_configs` WHERE `delete_time` IS NULL;",
);

View File

@@ -0,0 +1,12 @@
if (!payload.get().path.includes('auth')) {
if (!payload.get().headers['Cache-Control'] || !JSON.stringify(payload.get().headers['Cache-Control']).includes('skip-cache')) {
cache.set('results', signature, res, TABLE_INFO.cache_life * 1000)
// var currentTime = new Date();
// currentTime.setSeconds(currentTime.getSeconds() + TABLE_INFO.cache_life);
// var timeAfter = currentTime.toUTCString();
// response.headers.set("Expires", timeAfter)
response.headers.set("Cache-Status", "set")
response.headers.set("Cache-Key", signature)
response.headers.set("Cache-Control", `no-transform,no-siteapp,max-age=${TABLE_INFO.cache_life}`)
}
}

View File

@@ -0,0 +1,69 @@
// 【跨域配置】
response.headers.set("Origin", "*");
response.headers.set("Timing-Allow-Origin", "*");
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set("Vary", "Etag, Save-Data, Accept-Encoding") // Vary 头用于告知客户端在进行请求时需要考虑的头部字段。
response.headers.set("Access-Control-Allow-Headers", '*');
response.headers.set("Access-Control-Allow-Methods", '*');
response.headers.set("Access-Control-Allow-Credentials", true); // 设置允许跨域携带凭证
response.headers.set("Access-Control-Expose-Headers", '*');
response.headers.set("Access-Control-Request-Method", '*');
response.headers.set("Access-Control-Request-Headers", '*');
// response.headers.set("Cross-Origin-Embedder-Policy", "require-corp") // 设置跨域嵌入策略
response.headers.set("Cross-Origin-Opener-Policy", "same-origin") // 设置跨域 opener 策略
response.headers.set("Cross-Origin-Resource-Policy", "cross-origin") // 设置跨域资源策略
// 【安全配置】
// response.headers.set("Content-Security-Policy", "default-src 'none'; script-src'self'; style-src'self'; img-src'self'; font-src'self'; connect-src'self'; frame-src'self'; media-src'self'; object-src'self'; manifest-src'self'; worker-src'self'; base-uri'self'; form-action'self'; frame-ancestors'self';")
//X-Permitted-Cross-Domain-Policies
response.headers.set("X-Permitted-Cross-Domain-Policies", "none") // 设置跨域策略
response.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") // 启用严格传输安全
response.headers.set("X-Frame-Options", "sameorigin") // 防止点击劫持
response.headers.set("X-XSS-Protection", "1; mode=block") // 防止 XSS 攻击
response.headers.set("Referrer-Policy", "origin-when-cross-origin") // 防止 referrer 泄漏
response.headers.set("X-Content-Type-Options", "nosniff") // 防止 MIME 类型嗅探
response.headers.set("X-DNS-Prefetch-Control", "auto") // 防止 DNS 预取取
response.headers.set("X-Download-Options", "noopen") // 防止下载时自动打开
response.headers.set("X-Frame-Options", "sameorigin") // 防止点击劫持 - 不允许从其他域加载 - 不允许被其它域iframe
// 【性能配置】
// response.headers.set("Accept-Ranges", "bytes") // 设置接受范围 [暂不支持]
// response.status.earlyHints() // 设置预加载
// response.headers.set("Link",'</font.ttf>; rel=preload; as=font') // 设置预加载字体
// response.headers.set("Link",'</image.png>; rel=preload; as=image') // 设置预加载图片
// response.headers.set("Link",'</audio.mp3>; rel=preload; as=audio') // 设置预加载音频
// response.headers.set("Link",'</video.mp4>; rel=preload; as=video') // 设置预加载视频
// response.headers.set("Link",'</style.css>; rel=preload; as=style') // 设置预加载样式
// response.headers.set("Link",'</script.js>; rel=preload; as=script') // 设置预加载脚本
// response.headers.set("Link",'</document.pdf>; rel=preload; as=document') // 设置预加载文档
// response.write('')
response.headers.set("Access-Control-Max-Age", "31536000") // 设置最大缓存预检时间为一年
// response.headers.set("Connection", "keep-alive") // 设置连接保持 *⚠️️ HTTP2 已标记该特性废弃
// response.headers.set("Keep-Alive", "timeout=60, max=1000") // 设置连接保持时间为60秒最大连接数为1000 *⚠️️ HTTP2 已标记该特性废
// 设置为 time 后的一年过期
// time = new Date(time.getFullYear() + 1, time.getMonth(), time.getDate(), time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds());
// timeStr = time.toUTCString()
// response.headers.set("Expires", timeStr)
response.headers.set("Cache-Control", "no-transform,no-siteapp,max-age=31536000") // 设置缓存时间为一年并屏蔽搜索引擎的预取
if (payload.get().headers['If-Match']) {
response.headers.set("If-Match", payload.get().headers['If-Match'][0])
}
if (payload.get().headers['If-None-Match']) {
response.headers.set("If-None-Match", payload.get().headers['If-None-Match'][0])
}
// 【其它配置】
response.headers.set("Alt-Svc", 'h2=":443"; ma=2592000;') // 设置服务端推送
response.headers.set("Alt-Used", 'h2=":443";') // 设置服务端推送
response.headers.set("X-Quic", 'h3')
response.headers.set("Accept-Ch", "Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, DPR, Viewport-Width, Width, ETag, If-Match, If-None-Match") // 设置请求头
// 声明请求ID,同时声明SUID的节点编号 范围0-1023当分布式服务时避免SUID冲突。SUID首次生成后既确定NODEID后续的请求将使用该NODEID重复声明的NODEID将被忽略
response.headers.set('X-Request-Id', ex.suid(0).base58())

View File

@@ -0,0 +1,24 @@
// 如果存在Origin头则优先使用Origin头中的域名否则使用Referer头中的域名。
let domain = payload.get().headers["Origin"]
if (!domain) {
domain = payload.get().headers["Referer"]
}
if (domain) {
let ref = new URL(domain)
// console.log("Source:", ref.hostname)
// 如果不匹配则返回403错误。
// if (!ref.hostname.endsWith('qq.com') &&
// !ref.hostname.endsWith('l-l.cn') &&
// !ref.hostname.endsWith('dx5.cn') &&
// !ref.hostname.endsWith('localhost') &&
// !ref.hostname.endsWith('127.0.0.1')) {
// console.error(domain)
// return errMsg(403, "未授权的请求")
// }
} else {
domain = "*"
// 是否允许Origin 或 Referer 为空 - 如果不允许将返回403错误。如果不允许则打开注释
// return errMsg(403, "未授权的请求")
}

196
server/api/lib/sql.js Normal file
View File

@@ -0,0 +1,196 @@
// ==========================================================================
// 所属模块lib - sql
// 生成日期2024-02-19 00:24:00 +0800 CST
// 生成路径: lib/sql.js
// 生成人Cato
// 功能描述定义全局通用的SQL的CURD功能封装包含分页列表和模版查询、数据增删等等。注册于global.js内)
// ==========================================================================
// 系统的sql数据库连接名为 DB_NAME
// 连接测试数据库
// var test_sql = SQL.new(
// 'mydb',
// 'mysql',
// 'root:123456@tcp(127.0.0.1:3306)/test_db'
// )
function selectData(condition) {
const newPlaceholders = getPlaceholders(condition).placeholders;
if (newPlaceholders) {
const tempParams = getPlaceholders(condition).tempParams;
return {
query: `SELECT ${TABLE_FIELDS ? TABLE_FIELDS : '*'} FROM ${TABLE_NAME} WHERE ${newPlaceholders} AND delete_time IS NULL LIMIT ?,?`,
params: [...tempParams, (condition.pageNum - 1) * condition.pageSize, condition.pageSize],
};
} else {
return {
query: `SELECT ${TABLE_FIELDS ? TABLE_FIELDS : '*'} FROM ${TABLE_NAME} WHERE delete_time IS NULL LIMIT ?,?`,
params: [(condition.pageNum - 1) * condition.pageSize, condition.pageSize],
};
}
}
function countTotal(condition) {
const newPlaceholders = getPlaceholders(condition).placeholders;
const tempParams = getPlaceholders(condition).tempParams;
if (newPlaceholders) {
return {
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE ${newPlaceholders} AND delete_time IS NULL `,
params: [...tempParams],
};
} else {
return {
query: `SELECT COUNT(*) AS count FROM ${TABLE_NAME} WHERE delete_time IS NULL`,
params: [],
};
}
}
function sql_select() {
const data = payload.get().query;
if (!data.pageSize || !data.pageNum) {
// 根据id查询详情
var useParent = scriptPath != payload.get().path.substring(1);
if (useParent) {
let value = payload.get().path.split('/').pop();
const result = SQL.query(
DB_NAME,
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
value
);
if (result.length) {
const newResult = {};
Object.keys(result[0]).forEach((item) => {
newResult[underscoreToCamel(item)] = result[0][item];
});
return okMsg(newResult);
}
return errMsg(507, '获取失败');
}
}
// 格式化这个query参数
let newData = {};
Object.keys(data).forEach((item) => {
newData[item] = data[item][0];
});
// 总数据
const ROWS_COUNT = countTotal(newData);
const count = SQL.query(DB_NAME, ROWS_COUNT.query, ...ROWS_COUNT.params);
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
// 格式化数据
const results = result.map((row) => {
const result = {};
Object.keys(row).forEach((col) => {
result[underscoreToCamel(col)] = row[col];
});
return result;
});
return okMsg({
data: results,
count: Number(count[0]?.count),
pageNum: Number(newData.pageNum),
pageSize: Number(newData.pageSize),
});
}
function sql_update() {
const data = payload.get().body?.toObject();
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
let filter = payload.get().path.split('/').pop();
const columnsArr = Object.keys(newData);
let dataArr = [];
const placeholders = [];
columnsArr.forEach((item) => {
if (newData[item]) {
placeholders.push(`${item} = ?`);
dataArr.push(newData[item]);
} else {
placeholders.push(`${item} = NULL`);
}
});
const newPlaceholders = placeholders.join(',');
const query = `UPDATE ${TABLE_NAME} SET ${newPlaceholders} WHERE ${COLUMN_KEY} = ?`;
const res = SQL.exec(DB_NAME, query, ...[...dataArr, filter]);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}
function sql_insert() {
const data = payload.get().body?.toObject();
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = [];
if (!data[COLUMN_KEY] || data[COLUMN_KEY] !== "auto") {
columnsArr.push(COLUMN_KEY);
dataArr.push(ex.suid().base58());
}
// columnsArr.push(COLUMN_KEY)
// columnsArr.push('proprietor_id')
// columnsArr.push('proprietor_name')
// dataArr.push(ex.suid().base58())
// dataArr.push(USER_INFO.user_id)
// dataArr.push(USER_INFO.nickname)
dataArr.forEach(() => {
placeholders.push('?');
});
placeholders.join(',');
const columnsStr = columnsArr.join(',');
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec(DB_NAME, query, ...dataArr);
if (res) {
return okMsg(res);
}
return errMsg(507, '添加失败');
}
function sql_delete() {
let filter = payload.get().path.split('/').pop();
let query;
let params = filter.split(',');
// 兼容批量删除
const tables = params.map((item) => '?');
query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} IN (${tables.join(',')});`;
const res = SQL.exec(DB_NAME, query, ...params);
if (res) {
return okMsg(res);
}
return errMsg(507, '删除失败');
}
// 基本的CURD功能
function base_curd() {
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'PUT':
return sql_update();
case 'POST':
return sql_insert();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,15 @@
if (payload.get().method != "OPTIONS") {
let appId = payload.get().headers["AppId"]
if (!appId) {
appId = "hVTfgGLRhv7" // 默认AppId
}
APP_INFO = SQL.cache.query(
'system_sql',
86400, // 缓存时间单位秒0表示不缓存-1表示永久缓存默认缓存时间为1天
"SELECT * FROM `sys_passport` WHERE `app_id` = ? AND `status` > 0;", appId
);
if (!APP_INFO.length) {
return errMsg(403, "未授权的访问")
}
APP_INFO = APP_INFO[0]
}

View File

@@ -0,0 +1,63 @@
// ==========================================================================
// 数据表log_logins - 系统菜单
// ==========================================================================
TABLE_NAME = 'log_logins';
COLUMN_KEY = 'info_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'loginLocation') {
placeholders.push(`login_location LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'username') {
placeholders.push(`username LIKE ?`);
tempParams.push('%' + condition[item] + '%');
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function del() {
let filter = payload.get().path.split('/').pop();
let res;
if (filter != 'list') {
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} = ?`;
res = SQL.exec(DB_NAME, query, filter);
} else {
// 清空
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE delete_time IS NULL`;
res = SQL.exec(DB_NAME, query);
}
if (res) {
return okMsg(res);
}
return errMsg(507, '删除失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'DELETE':
return del();
case 'POST':
return sql_insert();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,66 @@
// ==========================================================================
// 数据表log_opers - 系统菜单
// ==========================================================================
TABLE_NAME = 'log_opers';
COLUMN_KEY = 'oper_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'title') {
placeholders.push(`title LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'operName') {
placeholders.push(`oper_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'businessType') {
placeholders.push(`business_type = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function del() {
let filter = payload.get().path.split('/').pop();
let res;
if (filter != 'list') {
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE ${COLUMN_KEY} = ?`;
res = SQL.exec(DB_NAME, query, filter);
} else {
// 清空
const query = `UPDATE ${TABLE_NAME} SET delete_time = NOW() WHERE delete_time IS NULL`;
res = SQL.exec(DB_NAME, query);
}
if (res) {
return okMsg(res);
}
return errMsg(507, '删除失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'DELETE':
return del();
case 'POST':
return sql_insert();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,98 @@
// ==========================================================================
// 数据表sys_menus - 系统菜单
// ==========================================================================
TABLE_NAME = 'sys_menus';
COLUMN_KEY = 'menu_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'menuName') {
placeholders.push(`menu_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function selectData(condition) {
const newPlaceholders = getPlaceholders(condition).placeholders;
if (newPlaceholders) {
const tempParams = getPlaceholders(condition).tempParams;
return {
query: `SELECT * FROM sys_menus WHERE ${newPlaceholders} AND delete_time IS NULL ORDER BY sort ASC`,
params: [...tempParams],
};
} else {
return {
query: `SELECT * FROM sys_menus WHERE delete_time IS NULL ORDER BY sort ASC`,
params: [],
};
}
}
function list() {
const data = payload.get().query;
// 格式化这个query参数
let newData = {};
Object.keys(data).forEach((item) => {
newData[item] = data[item][0];
});
const LIST_DATA = selectData(newData);
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
// 格式化数据
const newResult = result.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatMenuData(newResult);
return okMsg(formatRes);
}
function formatMenuData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatMenuData(data, item.menuId),
}));
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list();
case 'PUT':
return sql_update();
case 'POST':
return sql_insert();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,28 @@
function formatMenuData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatMenuData(data, item.menuId),
}));
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const result = SQL.query(DB_NAME, `SELECT menu_id,menu_name,parent_id FROM sys_menus WHERE delete_time IS NULL ORDER BY sort ASC`);
// 格式化数据
const newResult = result.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatMenuData(newResult);
return okMsg(formatRes);
}

View File

@@ -0,0 +1,34 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let filter = payload.get().path.split('/').pop();
const res1 = SQL.query('system_sql', 'SELECT menu_id FROM sys_role_menus_rel WHERE delete_time IS NULL AND role_id = ?', filter);
const res = SQL.query('system_sql', 'SELECT menu_id, menu_name,parent_id FROM sys_menus WHERE delete_time IS NULL');
if (res && res1) {
const checkedKeys = res1.map((item) => item.menu_id);
// 格式化数据
const newResult = res.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatOrgData(newResult);
return okMsg({ checkedKeys, menus: formatRes });
}
return errMsg(404, '查询失败');
}
function formatOrgData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatOrgData(data, item.menuId),
}));
}

View File

@@ -0,0 +1,35 @@
// ==========================================================================
// 数据表sys_notices - 公告通知
// ==========================================================================
TABLE_NAME = 'sys_notices';
COLUMN_KEY = 'notice_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'title') {
placeholders.push(`title LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'noticeType') {
placeholders.push(`notice_type = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return base_curd()
}

View File

@@ -0,0 +1,33 @@
function main() {
const corpid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_corpid').value;
const appkey = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appkey').value;
const appid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_clientId').value;
const appsecret = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appsecret').value;
const req = {
url: `https://oapi.dingtalk.com/gettoken?appkey=${appkey}&appsecret=${appsecret}`
}
const res = request.parse(req).body.toObject()
// {
// "errcode": 0,
// "access_token": "9c499abd27fe373b8c8f1fbed327ba4e",
// "errmsg": "ok",
// "expires_in": 7200
// }
if (res.access_token) {
return okMsg(res.access_token)
}
cache.set('oauth', 'dingtalk', res.access_token, 7000 * 1000)
os.sleep(7000)
main()
}
// const access_token = cache.get('oauth', document.phone)
// if (access_token == null) {
// return false
// }

View File

@@ -0,0 +1,6 @@
response.headers.add('X-Script-Version', 'open-demo-1.0')
function main() {
return okMsg(payload.get())
// return JSON.stringify(SQL.query(DB_NAME, "select uuid()"))
}

View File

@@ -0,0 +1,7 @@
response.headers.add('X-Script-Version', 'open-upyun-1.0')
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return okMsg('ok')
}

View File

@@ -0,0 +1,101 @@
// ==========================================================================
// 数据表sys_organizations - 系统接口数据
// ==========================================================================
TABLE_NAME = 'sys_organizations';
COLUMN_KEY = 'organization_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'organizationName') {
placeholders.push(`organization_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function selectData(condition) {
const newPlaceholders = getPlaceholders(condition).placeholders;
if (newPlaceholders) {
const tempParams = getPlaceholders(condition).tempParams;
return {
query: `SELECT * FROM sys_organizations WHERE ${newPlaceholders} AND delete_time IS NULL ORDER BY sort ASC`,
params: [...tempParams],
};
} else {
return {
query: `SELECT * FROM sys_organizations WHERE delete_time IS NULL ORDER BY sort ASC`,
params: [],
};
}
}
function list() {
const data = payload.get().query;
// 格式化这个query参数
let newData = {};
Object.keys(data).forEach((item) => {
newData[item] = data[item][0];
});
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query(DB_NAME, LIST_DATA.query, ...LIST_DATA.params);
// 格式化数据
const newResult = result.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatOrgData(newResult);
return okMsg(formatRes);
}
function formatOrgData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatOrgData(data, item.organizationId),
}));
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list();
case 'PUT':
return sql_update();
case 'POST':
return sql_insert();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,36 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let filter = payload.get().path.split('/').pop();
const res1 = SQL.query('system_sql', 'SELECT organization_id FROM sys_role_organizations_rel WHERE delete_time IS NULL AND role_id = ?', filter);
const res = SQL.query('system_sql', 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL');
if (res && res1) {
const checkedKeys = res1.map((item) => item.organization_id);
// 格式化数据
const newResult = res.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatOrgData(newResult);
return okMsg({ checkedKeys, organizations: formatRes });
}
return errMsg(404, '查询失败');
}
function formatOrgData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatOrgData(data, item.organizationId),
}));
}

View File

@@ -0,0 +1,50 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const filter = payload.get().query;
let orgSql
let res
if (!fileter) {
// 查根元素
orgSql = 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL AND parent_id = 0 OR parent_id IS NULL'
} else {
if (fileter.parentId) {
// 查子元素
orgSql = 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL AND parent_id = ?'
} else {
// 根据orgId查用户信息
res = 'SELECT user_id, username FROM sys_users WHERE delete_time IS NULL AND organization_id = ?'
}
}
// let filter = payload.get().path.split('/').pop();
const res1 = SQL.query('system_sql', 'SELECT organization_id FROM sys_role_organizations_rel WHERE delete_time IS NULL AND role_id = ?', filter);
res = SQL.query('system_sql', 'SELECT organization_id, organization_name,parent_id FROM sys_organizations WHERE delete_time IS NULL');
if (res && res1) {
const checkedKeys = res1.map((item) => item.organization_id);
// 格式化数据
const newResult = res.map((itemO) => {
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
return {
...newObj,
parentId: newObj.parentId == 0 ? null : newObj.parentId
};
});
const formatRes = formatOrgData(newResult);
return okMsg({ checkedKeys, organizations: formatRes });
}
return errMsg(404, '查询失败');
}
function formatOrgData(data, parentId = null) {
return data
.filter((item) => item.parentId == parentId)
.map((item) => ({
...item,
children: formatOrgData(data, item.organizationId),
}));
}

106
server/api/page/function.js Normal file
View File

@@ -0,0 +1,106 @@
/** 页面管理基础接口 **/
TABLE_NAME = 'sys_pages';
COLUMN_KEY = 'page_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js';
switch (payload.get().method) {
case "GET":
return list();
case "POST":
return insert();
case "PUT":
return sql_update();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'title') {
placeholders.push(`title LIKE ?`);
tempParams.push('%' + condition[item] + '%');
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function list() {
const queryPa = payload.get().query;
if (!Object.keys(queryPa).length) {
// 根据id查询详情
var useParent = scriptPath != payload.get().path.substring(1);
if (useParent) {
let value = payload.get().path.split('/').pop();
const result = SQL.query(
'system_sql',
`SELECT * FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
value
);
if (result.length) {
return okMsg(result);
}
return errMsg('获取失败');
}
}
// 格式化这个query参数
let newData = {};
Object.keys(queryPa).forEach((item) => {
newData[item] = queryPa[item][0];
});
// 总数据
const COUNT_TOTAL = countTotal(newData);
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
return okMsg({ result, count: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
}
function insert() {
const data = payload.get().body?.toObject();
const newData = { ...data };
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = [];
columnsArr.push(COLUMN_KEY)
columnsArr.push('proprietor_id')
columnsArr.push('proprietor_name')
const suid = ex.suid().base58()
dataArr.push(suid)
dataArr.push(USER_INFO.user_id)
dataArr.push(USER_INFO.nickname)
dataArr.forEach(() => {
placeholders.push('?');
});
placeholders.join(',');
const columnsStr = columnsArr.join(',');
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec('system_sql', query, ...dataArr);
if (res) {
return okMsg(suid);
}
return errMsg(507, '添加失败');
}

View File

@@ -0,0 +1,39 @@
// ==========================================================================
// 数据表oa_posts - 系统接口数据
// ==========================================================================
TABLE_NAME = 'oa_posts';
COLUMN_KEY = 'post_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'postName') {
placeholders.push(`post_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'postCode') {
placeholders.push(`post_id = ?`);
tempParams.push(condition[item]);
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
return base_curd();
}

View File

@@ -0,0 +1,12 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().body?.toObject();
const query = `UPDATE sys_roles SET status = ? WHERE role_id = ?`;
const res = SQL.exec('system_sql', query, data.status, data.roleId);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}

View File

@@ -0,0 +1,75 @@
// 角色数据权限修改
// 对应字典:
//[
// {
// value: '0', // 本组织id
// label: '仅本人数据权限',
// },
// {
// value: '1', // 所有组织id
// label: '全部数据权限',
// },
// {
// value: '2', // 参数组织id
// label: '自定数据权限',
// },
// {
// value: '3', // 本组织id
// label: '本组织数据权限',
// },
// {
// value: '4', // 不存
// label: '本组织及以下数据权限',
// },
// ],
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().body?.toObject();
// 更新role表
const query = `UPDATE sys_roles SET data_scope = ? WHERE role_id = ?`;
const res = SQL.exec('system_sql', query, data.dataScope, data.roleId);
let orgRes = true;
if (!res) {
return errMsg(507, '更新失败');
}
// 更新role_org表
let orgIds = [];
if (data.dataScope == '0') {
// 通过本组织id创建
} else if (data.dataScope == '1') {
// 查询所有组织id
orgIds = SQL.query('system_sql', 'SELECT organization_id FROM sys_organizations ');
} else if (data.dataScope == '2') {
// 参数组织id
orgIds = data.organizationIds;
} else if (data.dataScope == '3') {
// 通过本组织id创建
} else if (data.dataScope == '4') {
// 不存
orgIds = [];
}
// 删除旧数据
const delRes = SQL.exec('system_sql', 'DELETE FROM sys_role_organizations_rel WHERE role_id = ?', data.roleId);
if (orgIds.length) {
let insertStr = '';
let valueStrArr = [];
let valueArr = [];
for (i = 0; i < orgIds.length; i++) {
valueStrArr.push(`(?,?,?,?,?)`);
valueArr.push(ex.suid().base58(), data.roleId, orgIds[i], USER_INFO.user_id, USER_INFO.nickname);
}
insertStr += `INSERT INTO sys_role_organizations_rel (rule_id, role_id, organization_id, proprietor_id, proprietor_name) VALUES ${valueStrArr.join(',')};`;
orgRes = SQL.exec('system_sql', insertStr, ...valueArr);
}
if (orgRes) {
return okMsg(orgRes);
}
return errMsg(507, '更新失败');
}

View File

@@ -0,0 +1,165 @@
// ==========================================================================
// 所属模块SysRole
// 生成日期2024-03-24 16:10:31 +0800 CST
// 生成路径: /role/list/function.js
// 生成人xScript_Engine
// 数据表sys_roles - 角色表
// ==========================================================================
TABLE_NAME = 'sys_roles';
COLUMN_KEY = 'role_id';
TABLE_FIELDS = '*';
var selectDetailValue = 'role_id,role_name,status,role_key,data_scope,role_sort,update_by,remark';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'roleName') {
placeholders.push(`role_name LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'roleKey') {
placeholders.push(`role_key LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function update() {
const data = payload.get().body?.toObject();
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
let filter = payload.get().path.split('/').pop();
const columnsArr = Object.keys(newData);
let dataArr = [];
const placeholders = [];
columnsArr.forEach((item) => {
if (newData[item] && item != 'menu_ids' && item != 'api_ids') {
placeholders.push(`${item} = ?`);
dataArr.push(newData[item]);
} else if (!newData[item]) {
placeholders.push(`${item} = NULL`);
}
});
const newPlaceholders = placeholders.join(',');
// 更新role表
const query = `UPDATE sys_roles SET ${newPlaceholders} WHERE role_id = ?`;
const res = SQL.exec('system_sql', query, ...[...dataArr, filter]);
let menuRes = true;
let apiRes = true;
// 更新role_menu表
if (newData.menu_ids.length && res) {
let delRes;
const delPre = SQL.query('system_sql', 'SELECT role_id FROM sys_role_menus_rel WHERE role_id = ?', filter);
if (delPre.length) {
delRes = SQL.exec('system_sql', 'DELETE FROM sys_role_menus_rel WHERE role_id = ?', filter);
}
let valueStrArr = [];
const valueArr = [];
for (let i = 0; i < newData.menu_ids.length; i++) {
valueStrArr.push(`(?,?,?)`);
valueArr.push(ex.suid().base58(), filter, newData.menu_ids[i]);
}
menuRes = SQL.exec('system_sql', `INSERT INTO sys_role_menus_rel (rule_id, role_id, menu_id) VALUES ${valueStrArr.join(',')}`, ...valueArr);
}
// 更新api权限相关表
if (newData.api_ids.length && res) {
SQL.exec('system_sql', 'DELETE FROM casbin_rule WHERE role_id = ?', newData.role_id);
let valueStrArr = [];
const valueArr = [];
for (let i = 0; i < newData.api_ids.length; i++) {
valueStrArr.push(`(?,?,?,?,?)`);
valueArr.push(ex.suid().base58(), newData.role_id, newData.role_key, newData.api_ids[i].method, newData.api_ids[i].path);
}
apiRes = SQL.exec('system_sql', `INSERT INTO casbin_rule (rule_id, role_id, role_key, method, patterns) VALUES ${valueStrArr.join(',')};`, ...valueArr);
}
if (res && menuRes && apiRes) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}
function insert() {
const data = payload.get().body?.toObject();
const newData = {};
Object.keys(data).forEach((item) => {
newData[camelToUnderscore(item)] = data[item];
});
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = ['?'];
const newColumnsArr = ['role_id'];
const newDataArr = [ex.suid().base58()];
columnsArr.forEach((item) => {
if (item != 'menu_ids' && item != 'api_ids') {
newColumnsArr.push(item);
placeholders.push('?');
newDataArr.push(newData[item]);
}
});
placeholders.join(',');
const columnsStr = newColumnsArr.join(',');
const query = `INSERT INTO sys_roles (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec('system_sql', query, ...newDataArr);
let menuRes = true;
let apiRes = true;
// role_menu表插入
if (newData.menu_ids.length && res) {
let valueStrArr = [];
const valueArr = [];
for (i = 0; i < newData.menu_ids.length; i++) {
valueStrArr.push(`(?,?,?)`);
valueArr.push(ex.suid().base58(), res, newData.menu_ids[i]);
}
menuRes = SQL.exec('system_sql', `INSERT INTO sys_role_menus_rel (rule_id, role_id, menu_id) VALUES ${valueStrArr.join(',')}`, ...valueArr);
}
// api权限相关表插入数据
if (newData.api_ids.length && res) {
let valueStrArr = [];
const valueArr = [];
for (i = 0; i < newData.api_ids.length; i++) {
valueStrArr.push(`(?,?,?,?,?)`);
valueArr.push(ex.suid().base58(), newData.role_id, newData.role_key, newData.api_ids[i].method, newData.api_ids[i].path,);
}
apiRes = SQL.exec('system_sql', `INSERT INTO casbin_rule (rule_id, role_id, role_key, method, patterns) VALUES ${valueStrArr.join(',')};`, ...valueArr);
}
if (res && menuRes && apiRes) {
return okMsg(res);
}
return errMsg(507, '添加失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return sql_select();
case 'PUT':
return update();
case 'POST':
return insert();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,34 @@
function main() {
function upInsert() {
const data = payload.get().body?.toObject()
// 查询是否已存在
const sqlSelect = `SELECT * FROM oa_pm_project_budget WHERE sub_id = '${data.sub_id}' AND project_id = '${data.project_id}';`
const selectRes = SQL.query(DB_NAME, sqlSelect)
if (selectRes && selectRes.length) {
const sqlUpdate = `UPDATE oa_pm_project_budget
SET project_name = '${data.project_name}', net_value = ${data.net_value}, tax_value = ${data.tax_value}, tip = '${data.tip}'
WHERE sub_id = '${data.sub_id}' AND project_id = '${data.project_id}'`
const updateRes = SQL.exec(DB_NAME, sqlUpdate)
if (updateRes) {
return okMsg(updateRes)
} else {
return errMsg(insertRes)
}
}
const sqlInsert = `INSERT INTO oa_pm_project_budget (sub_id, project_id, project_name, net_value, tax_value, tip) VALUES (?, ?, ?, ?, ?, ?)`
const insertRes = SQL.exec(DB_NAME, sqlInsert, ...[data.sub_id, data.project_id, data.project_name, data.net_value, data.tax_value, data.tip])
if (insertRes) {
return okMsg(insertRes)
} else {
return errMsg(insertRes)
}
}
switch (payload.get().method) {
case 'POST':
return upInsert()
}
}

View File

@@ -0,0 +1,40 @@
// ==========================================================================
// 所属模块Upload/Token
// 生成日期2024-02-08 17:09:21 +0800 CST
// 生成路径: upload/token/function.js
// 生成人xScript_Engine
// 数据表sys_files - 文件上传(又拍云)
// ==========================================================================
response.headers.add('X-Script-Version', 'upload-1.0');
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const file_id = payload.get().path.split('/').pop();
const params = payload.get().query; // 获取请求参数
if (!file_id || !params.signature) {
return errMsg(400, '缺少参数');
}
const sql = `INSERT INTO sys_files (file_id, file_name, file_path, file_type, file_size, file_hash, proprietor_id, proprietor_name, privacy,is_folder,parent_id) VALUES (?, ?, ?, ? , ? , ?, ?, ?, ?, ?, ?);`;
let result = SQL.exec(
'system_sql',
sql,
file_id,
params.name,
params.path,
params.type,
params.size,
params.signature,
USER_INFO.user_id,
USER_INFO.nickname,
params.privacy ? params.privacy : 0,
params.isFolder ? params.isFolder : 0,
params.parentId ? params.parentId : 0
);
if (result) {
return okMsg(true)
}
return errMsg(400, '上传失败')
}

View File

@@ -0,0 +1,99 @@
// ==========================================================================
// 所属模块Uploads
// 生成日期2024-02-08 17:09:21 +0800 CST
// 生成路径: uploads/function.js
// 生成人xScript_Engine
// 数据表sys_files - 文件上传
// ==========================================================================
response.headers.add('X-Script-Version', 'uploads-1.0');
function upload() {
const params = payload.get().query;
const file_id = ex.suid().base58();
let file_path = '/uploads/' + file_id;
let upload_result
if (!params.signature) {
return errMsg(400, '缺少参数');
}
const sql = `SELECT * FROM sys_files WHERE file_hash =? LIMIT 1;`;
let res = SQL.query('system_sql', sql, params.signature);
if (res.length > 0) {
res = res[0]
let file_name = payload.get().path.split('/').pop()
file_path = res.file_path;
upload_result = {
name: file_name,
type: res.file_type,
size: res.file_size,
};
}
else {
upload_result = response.upload(rootPath + file_path);
upload_result.new = true;
}
if (upload_result.name) {
const sql = `INSERT INTO sys_files (file_id, file_name, file_path, file_type, file_size, file_hash, proprietor_id, proprietor_name, privacy,is_folder,parent_id) VALUES (?, ?, ?, ? , ? , ?, ?, ?, ?, ?, ?);`;
SQL.exec(
'system_sql',
sql,
file_id,
upload_result.name,
file_path,
upload_result.type,
upload_result.size,
params.signature,
USER_INFO.user_id,
USER_INFO.nickname,
params.privacy ? params.privacy : 0,
params.isFolder ? params.isFolder : 0,
params.parentId ? params.parentId : 0
);
// if (upload_result.new) {
// return ''
// }
return okMsg({ id: file_id, name: upload_result.name, type: upload_result.type, size: upload_result.size })
}
return errMsg(507, '上传失败');
}
function info() {
const fileId = payload.get().query.fileId;
const res = SQL.query('system_sql', `SELECT * FROM sys_files WHERE delete_time IS NULL AND file_id = ?;`, fileId);
return JSON.stringify({
filePath: res[0].file_path,
fileType: res[0].file_type,
fileSize: res[0].file_size,
});
}
function del() {
const fileId = payload.get().query.fileId;
const file_path = rootPath + '/uploads/' + fileId;
// 数据库更新
const res = SQL.exec('system_sql', `UPDATE sys_files SET delete_time = NOW() WHERE file_id =?`, fileId);
if (res) {
return okMsg(res);
}
return errMsg(507, '删除失败');
}
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'POST':
return upload();
case 'DELETE':
return del();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,42 @@
// ==========================================================================
// 所属模块Upload/Token
// 生成日期2024-02-08 17:09:21 +0800 CST
// 生成路径: upload/token/function.js
// 生成人xScript_Engine
// 数据表sys_files - 文件上传(又拍云)
// ==========================================================================
response.headers.add('X-Script-Version', 'upload-1.0');
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
let content_md5 = payload.get().path.split("/").pop()
const sql = `SELECT * FROM sys_files WHERE file_hash =? LIMIT 1;`;
let res = SQL.query('system_sql', sql, content_md5);
if (res.length > 0) {
res = res[0]
okMsg({
path: res.file_path,
type: res.file_type,
size: res.file_size,
})
}
let password = crypto.hash.md5("v5JuNat8fget0184tFTTRH2Nyap35DiS").toHEX.encoding().toString();
// console.log(password);
let method = `PUT`
let file = `/${os.getEnv('BUCKET_NAME')}/${ex.suid().base58()}`
let now = new Date(Date.now()).toUTCString();
let digest = crypto.hash.hmac.sha1(password, `${method}&${file}&${now}&${content_md5}`).toBase64.encoding().toString();
// console.log(digest);
return okMsg({
file: file,
domain: 'cdn.p-q.co',
token: `UPYUN xmagic:${digest}`,
content_md5: content_md5
});
}

View File

@@ -0,0 +1,70 @@
// ==========================================================================
// 所属模块user - acl
// 生成日期2024-09-13 13:56:00 +0800 CST
// 生成路径: user/acl.js
// 生成人Cato
// 功能描述通过用户token取用户权限组、通过权限组id取具体的权限再判断用户是否有权限访问对应的路径。
// ==========================================================================
// 对于权限的判断先取Method再取Path最后取Token。
// 通过Token取得用户加入的用户组和用户的权限信息
// 权限数据在数据库中的权限组表存储为 roleId、name、method、patterns。
// 权限组在用户的存储为 UserID、roleIds权限组ID数组
// 取用户信息中的ruleIds字段然后根据ruleIds查询用户权限信息
// 判断用户是否有权限访问该路径,如果没有权限,则返回错误信息。
// method在数据库存储的格式为 GET、POST、PUT、DELETE
// patterns在数据库存储的格式为 /api/v1/xxx/xxx
// 当用户拥有更上一级目录的权限时,可以访问该目录下的所有子目录和文件。
// 例如 用户拥有 /api/v1/xxx 的权限,则可以访问 /api/v1/xxx/xxx/xxx 的路径。
// 是否允许公开访问
if (!ALLOW_PUBLIC_ACCESS) {
// 取 Authorization - Bearer 后面的Token值
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookie ? payload.get().cookie["token"] : null);
if (!userToken) {
return errMsg(413, "未登录");
}
// 判断userToken是否是数组
if (Array.isArray(userToken)) {
// userToken = userToken[0]
// userToken 取不为undefined的第一个值
userToken = userToken.find(item => item !== undefined)
}
// 删除 Bearer 部分
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
if (!tokenInfo) {
return errMsg(413, "身份验证失败");
}
// 判断tokenInfo.exp 是否过期
if (tokenInfo.exp < Date.now() / 1000) {
return errMsg(413, "身份凭证已过期");
}
// response.cookies.setRaw(`token=${userToken}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; PartitionKeySite=Main; HttpOnly; Secure`)
USER_INFO = SQL.query(
'system_sql',
"SELECT * FROM sys_users WHERE user_id = ?",
tokenInfo.uid
);
if (USER_INFO.length == 0) {
return errMsg(413, "用户不存在");
}
USER_INFO = USER_INFO[0]
if (OPERATION_TITLE, OPERATION_TYPE) {
let location = "未知"
SQL.push(
'system_sql', "INSERT INTO log_opers (oper_id, oper_name, business_type, title, method, oper_param, status, oper_ip, oper_url, oper_location) VALUES (?,?,?,?,?,?,?,?,?,?)",
ex.suid().base58(), tokenInfo.unm, OPERATION_TYPE, OPERATION_TITLE, payload.get().method, payload.get().body?.toString(), 0, payload.get().remote_addr, payload.get().path, location
)
}
}

View File

@@ -0,0 +1,12 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
const data = payload.get().body?.toObject();
const query = `UPDATE sys_users SET status = ? WHERE user_id = ?`;
const res = SQL.exec('system_sql', query, data.status, data.userId);
if (res) {
return okMsg(res);
}
return errMsg(507, '更新失败');
}

View File

@@ -0,0 +1,7 @@
function main() {
// const posts = SQL.query(DB_NAME, 'SELECT * FROM oa_posts WHERE delete_time IS NULL');
const roles = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL');
return okMsg({ roles: roles });
}

View File

@@ -0,0 +1,137 @@
// ==========================================================================
// 所属模块SysUsers
// 生成日期2024-03-24 16:10:31 +0800 CST
// 生成路径: /system/users/users/function.js
// 生成人xScript_Engine
// 数据表sys_users - 用户表(系统账户列表使用)
// ==========================================================================
TABLE_NAME = 'sys_users';
COLUMN_KEY = 'user_id';
TABLE_FIELDS = '*';
'use api/lib/sql.js';
function getPlaceholders(condition) {
const columnsArr = Object.keys(condition);
const placeholders = [];
const tempParams = [];
columnsArr.forEach((item) => {
if (item == 'nickname') {
placeholders.push(`nickname LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'phone') {
placeholders.push(`phone LIKE ?`);
tempParams.push('%' + condition[item] + '%');
} else if (item == 'status') {
placeholders.push(`status = ?`);
tempParams.push(condition[item]);
} else if (item == 'organizationId') {
placeholders.push(`organization_id = ?`);
tempParams.push(condition[item]);
}
});
return {
placeholders: placeholders.join(' AND '),
tempParams: tempParams,
};
}
function list() {
// 验证用户权限
// 'use api/user/acl/excerpt.js'
const data = payload.get().query;
if (!Object.keys(data).length) {
// 根据id查询详情
var useParent = scriptPath != payload.get().path.substring(1);
if (useParent) {
let value = payload.get().path.split('/').pop();
const result = SQL.query(
'system_sql',
`SELECT user_id,nickname,phone,role_id,role_key,role_name,email,organization_id,organization_name,post_id,post_name,remark,status,username,autograph,last_login_ip,last_login_time FROM ${TABLE_NAME} WHERE delete_time IS NULL AND ${COLUMN_KEY} = ?`,
value
);
// 获取岗位和角色
// const posts = SQL.query('system_sql', 'SELECT * FROM oa_posts WHERE delete_time IS NULL');
const roles = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL');
if (result.length) {
return okMsg({ data: result[0], roles });
}
return errMsg(507, '获取失败');
}
}
// 格式化这个query参数
let newData = {};
Object.keys(data).forEach((item) => {
newData[item] = data[item][0];
});
// 总数据
const COUNT_TOTAL = countTotal(newData);
const total = SQL.query('system_sql', COUNT_TOTAL.query, ...COUNT_TOTAL.params);
// 分页数据
const LIST_DATA = selectData(newData);
const result = SQL.query('system_sql', LIST_DATA.query, ...LIST_DATA.params);
return okMsg({ data: result, total: total[0].count, pageNum: Number(newData.pageNum), pageSize: Number(newData.pageSize) });
}
function insert() {
const data = payload.get().body?.toObject();
const newData = {};
console.log("crypto.bcrypt", crypto.bcrypt)
Object.keys(data).forEach((item) => {
if (camelToUnderscore(item) == 'password') {
newData[camelToUnderscore(item)] = crypto.bcrypt.encrypt(data[item]);
} else {
newData[camelToUnderscore(item)] = data[item];
}
});
const columnsArr = Object.keys(newData);
let dataArr = Object.values(newData);
const placeholders = [];
columnsArr.push(COLUMN_KEY)
columnsArr.push('proprietor_id')
columnsArr.push('proprietor_name')
dataArr.push(ex.suid().base58())
dataArr.push(USER_INFO.user_id)
dataArr.push(USER_INFO.nickname)
dataArr.forEach(() => {
placeholders.push('?');
});
placeholders.join(',');
const columnsStr = columnsArr.join(',');
const query = `INSERT INTO ${TABLE_NAME} (${columnsStr}) VALUES (${placeholders})`;
const res = SQL.exec('system_sql', query, ...dataArr);
if (res) {
return okMsg(res);
}
return errMsg(507, '添加失败');
}
function main() {
'use api/user/acl/excerpt.js'
switch (payload.get().method) {
case 'GET':
return list();
case 'PUT':
return sql_update();
case 'POST':
return insert();
case 'DELETE':
return sql_delete();
default:
response.status.notFound();
return `404 page not found`;
}
}

View File

@@ -0,0 +1,95 @@
// ------ 用户登录 --------
function main() {
if (!payload.get().headers['User-Agent']) {
return errMsg(413, '参数错误');
}
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
let ipAddr = payload.get().remote_addr.split(':')[0]
const data = payload.get().body?.toObject();
// 校验验证码
// if (!captcha.verify(data.captchaId, data.captcha)) {
// return errMsg(511, '验证码错误');
// }
let errorTime = cache.get('login', ipAddr)
let userErrorTime = cache.get('login', data.username)
errorTime = userErrorTime > errorTime ? userErrorTime : errorTime
// 用户密码错误3次封禁IP地址和用户名
if (errorTime > 3) {
return errMsg(403, '登录失败次数过多,请稍后再试');
}
let loginType = 'username'
if (data.username.match(/^\d+$/)) {
loginType = 'phone'
}
// 查找用户表
const res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE ${loginType} = ?`, data.username);
if (!res || !res.length) {
cache.set('login', data.username, errorTime + 1, 180 * 1000)
cache.set('login', ipAddr, errorTime + 1, 180 * 1000)
return errMsg(511, '用户或密码不正确');
}
const userData = res[0];
// 校验密码
if (!crypto.bcrypt.diff(data.password, userData.password)) {
cache.set('login', data.username, errorTime + 1, 1800)
cache.set('login', ipAddr, errorTime + 1, 1800 * 1000)
log(uaInfo, userData, ipAddr, 1)
return errMsg(511, '用户或密码不正确');
}
// 登录成功,清除错误次数
// cache.del('login', data.username)
// 根据角色id获取key
// const roleRes = SQL.query('system_sql', 'SELECT role_key FROM sys_roles WHERE role_id = ?', userData.role_id);
// 生成Token / 7天后过期
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
let token = jwt_issue(userData.user_id, userData.nickname);
// 更新最后登录时间和IP
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', ipAddr, userData.user_id);
// 记录登录日志
log(uaInfo, userData, ipAddr, 0)
// response.headers.set('Authorization', 'Bearer ' + token)
response.cookies.setRaw(`token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure`)
// return okMsg({ expire: sevenDays, token: token, userId: userData.user_id });
return okMsg({ expire: sevenDays, userId: userData.user_id });
}
// expire: 1714754886;
// token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjEsIlRlbmFudElkIjowLCJPcmdhbml6YXRpb25JZCI6MiwiVXNlck5hbWUiOiJyb290IiwiUm9sZUlkIjoxLCJSb2xlS2V5IjoiYWRtaW4iLCJEZXB0SWQiOjAsIlBvc3RJZCI6MSwiZXhwIjoxNzE0NzU0ODg2LCJpc3MiOiJ4TWFnaWMiLCJuYmYiOjE3MTQxNDkwODZ9.ZRidybNoZWKhUXkCeqZHtixO_oyzG56ZFtRKHPSU4lc';
// captcha: '1604';
// captchaId: 'BgsXvVYOUAD8dfKUfs2f';
// password: '123456';
// username: 'root';
function log(uaInfo, USER_INFO, addr, status) {
if (addr == null) {
return
}
// 记录登录日志
SQL.push('system_sql', 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
ex.suid().base58(),
USER_INFO.user_id,
USER_INFO.username,
status,
addr,
'未知',
uaInfo.browser,
uaInfo.os,
uaInfo.deviceModel,
payload.get().headers['User-Agent'][0],
);
}

View File

@@ -0,0 +1,6 @@
function main() {
response.headers.set("Clear-Site-Data", '"*"')
// response.cookies.setRaw("token=null; Path=/; Max-Age=-1; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure")
// response.redirect('/')
return okMsg(true);
}

View File

@@ -0,0 +1,83 @@
function main() {
// 验证用户权限
'use api/user/acl/excerpt.js'
// 获取角色和菜单
const role = SQL.query('system_sql', 'SELECT * FROM sys_roles WHERE delete_time IS NULL AND role_id = ?', USER_INFO.role_id);
const menus = SQL.query(
'system_sql',
`SELECT sm.*
FROM sys_role_menus_rel srm
JOIN sys_menus sm ON srm.menu_id = sm.menu_id
WHERE srm.role_id = ? AND srm.delete_time IS NULL`,
USER_INFO.role_id
);
const newUserInfo = {};
const newRoles = {};
const permissions = [];
Object.keys(USER_INFO).forEach((item) => {
newUserInfo[underscoreToCamel(item)] = USER_INFO[item];
});
// 从 newUserInfo 中删除不需要的属性
delete newUserInfo.password;
delete newUserInfo.salt;
Object.keys(role[0]).forEach((item) => {
newRoles[underscoreToCamel(item)] = role[0][item];
});
const newMenus = menus.map((itemO) => {
if (itemO.permission) {
permissions.push(itemO.permission);
}
const newObj = {};
Object.keys(itemO).forEach((item) => {
newObj[underscoreToCamel(item)] = itemO[item];
});
const meta = {
title: newObj.menuName,
isLink: newObj.isLink,
isHide: newObj.isHide == 1,
isAffix: newObj.isAffix != 1,
isIframe: newObj.isIframe != 1,
auth: [newObj.permission],
icon: newObj.icon,
};
return {
name: newObj.path,
path: newObj.path,
redirect: '',
component: newObj.component,
parentId: newObj.parentId == 0 ? null : newObj.parentId,
menuId: newObj.menuId,
menuType: newObj.menuType,
meta,
sort: newObj.sort
};
});
newMenus.sort((a, b) => a.sort - b.sort)
const formatRes = formatMenuData(newMenus);
// 推送预热大文件
// response.headers.set("Link", "<https://cdn.bootcdn.net/ajax/libs/font-awesome/6.6.0/css/all.css>; rel=preload; as=style")
// response.headers.set("Link", "<https://cdn.bootcdn.net/ajax/libs/crypto-js/4.2.0/crypto-js.min.js>; rel=preload; as=script")
// let dirInfo = fs.dir('app/assets')
// for (let i = 0; i < dirInfo.length; i++) {
// if (dirInfo[i].size > 1024 * 1000) { // 大小超过1MB
// response.headers.add("Link", `</app/assets/${dirInfo[i].name}>; rel=preload; as=${dirInfo[i].name.split('.').pop() == 'css' ? 'style' : 'script'}`)
// }
// }
return okMsg({ user: newUserInfo, role: newRoles, permissions: permissions, menus: formatRes });
}
function formatMenuData(data, parentId = null) {
const newData = data.filter((item) => item.parentId == parentId);
const coverData = [];
newData.forEach((item) => {
if (item.menuType != 'F')
coverData.push({
...item,
children: formatMenuData(data, item.menuId),
});
});
return coverData;
}

View File

@@ -0,0 +1,22 @@
function main() {
OPERATION_TITLE = "绑定钉钉账户"
OPERATION_TYPE = 5 // 0 未知 1 新增 2 修改 3 删除 4 查看 5 授权 6 审批 7 发布 8 导入 9 导出 10 打印 11 其他
// 验证用户权限
'use api/user/acl/excerpt.js'
const chiper = payload.get().query["chiper"];
if (!chiper) {
return errMsg(507, "参数错误");
}
// 解密chiper
let res = crypto.aes.decrypt('ECB', 'xMagic-oAuth_Wechat.1', chiper)
// 还原用户信息为对象
res = JSON.parse(res)
// 绑定用户
SQL.push('system_sql', "INSERT INTO sys_users_oauth (relate_id, user_id, login_from, open_id, username, avatar_img, raw_info) VALUES (?,?,?,?,?,?)",
ex.suid().base58(), tokenInfo.uid, 1, res.openid, res.nickname, res.headimgurl, JSON.stringify(res))
return okMsg("绑定成功")
}

View File

@@ -0,0 +1,158 @@
function main() {
const corpid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_corpid').value;
const appkey = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appkey').value;
const appid = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_clientId').value;
const appsecret = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_appsecret').value;
const accessToken = SYSTEM_CONFIG.find(item => item.name === 'dingtalk_access_token').value;
let domain = payload.get().headers["Origin"]
if (!domain) {
domain = payload.get().headers["Referer"]
}
if (payload.get().query.callback) {
response.headers.set("Location", `https://login.dingtalk.com/oauth2/auth?redirect_uri=${payload.get().query.callback || 'https://l-l.cn/app/login'}&response_type=code&client_id=${appkey}&scope=openid&state=dingtalk&prompt=consent`)
response.status.movedPermanently()
return ''
}
// 成功时跳转到https://www.aaaaa.com/a/b?authCode=xxxx&state=dddd
// 失败时跳转到https://www.aaaaa.com/a/b?error=yyyyyy&state=dddd
// if (payload.get().query["error"]) {
// return errMsg(511, `授权失败,错误: ${payload.get().query["error"]}`)
// }
// 通过回调Code换取AccessToken
let req = {
url: `https://api.dingtalk.com/v1.0/oauth2/userAccessToken`,
method: 'POST',
content_type: 'application/json',
data: JSON.stringify({
clientId: appkey,
clientSecret: appsecret,
code: payload.get().path.split('/').pop(),
// "refreshToken": "String",
grantType: "authorization_code"
})
}
// console.log(request.toCURL(req))
let res = request.parse(req).body.toObject()
// {
// "accessToken" : "abcd",
// "refreshToken" : "abcd",
// "expireIn" : 7200,
// "corpId" : "corpxxxx"
// }
// console.log(JSON.stringify(res))
if (!res.accessToken) {
// {"requestid":"AF130F96-1BB8-7FB1-A21B-2B29842D2D82","code":"invalidParameter.authCode.notFound","message":"不合法的临时授权码"}
return errMsg(511, `Token获取失败,错误代码: ${res.code},错误信息: ${res.message}`)
}
// 通过AccessToken换取用户信息
req = {
url: `https://api.dingtalk.com/v1.0/contact/users/me`,
headers: {
'x-acs-dingtalk-access-token': res.accessToken,
}
}
res = request.parse(req).body.toObject()
// {
// "nick" : "zhangsan",
// "avatarUrl" : "https://xxx",
// "mobile" : "150xxxx9144",
// "openId" : "123",
// "unionId" : "z21HjQliSzpw0Yxxxx",
// "email" : "zhangsan@alibaba-inc.com",
// "stateCode" : "86"
// }
// console.log(JSON.stringify(res))
// {"mobile":"17768938295","nick":"舒榆衡","openId":"XlH9GxRUh4oqoDBnf7OWWAiEiE","stateCode":"86","unionId":"ZQOFXUqiPZQcuSiPHZr45gJAiEiE","visitor":false}
if (!res.mobile) {
return errMsg(511, `Token验证不通过,错误代码: ${res.code},错误信息: ${res.message}`)
}
// 'use api/user/oauth/getUser.js'
// 绑定用户
// SQL.push('system_sql', "INSERT INTO sys_users_oauth (relate_id, user_id, login_from, open_id, union_id, username, avatar_img, raw_info) VALUES (?,?,?,?,?,?,?)",
// ex.suid().base58(), tokenInfo.uid, 1, res.openid, res.unionId, res.nick, res.avatarUrl, JSON.stringify(res))
// return okMsg("绑定成功")
// 查找用户表
res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE phone = ?;`, res.mobile);
// console.log(JSON.stringify(res))
// 生成Token / 7天后过期
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
let userData
// 如果用户在系统内已存在
if (res && res.length) {
userData = res[0];
// if (!userData.idno) {
// res = SQL.push('system_sql', `UPDATE sys_users SET real_name = ?,idno = ? WHERE phone = ?`, userInfo.clientname, userInfo.idnumber, userInfo.mobile)
// }
} else {
// if (!userInfo.mobile) {
return errMsg(503, '未绑定用户信息,请选择其他登录方式')
// }
// let userId = ex.suid().base58()
// res = SQL.exec('system_sql', `INSERT INTO sys_users (user_id,username,nickname,real_name,idno,phone) VALUES (?,?,?,?)`, userId, userInfo.idnumber, userInfo.clientname, userInfo.clientname, userInfo.idnumber, userInfo.mobile)
// if (!res) {
// return errMsg(500, '扫码注册失败')
// }
// userData = {
// user_id: userId,
// username: userInfo.idnumber,
// nickname: userInfo.clientname,
// phone: userInfo.mobile,
// role_id: 'hShxtTny1zo',
// role_key: '',
// }
}
let token = jwt_issue(userData.user_id, userData.nickname);
// 更新最后登录时间
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', userData.ip, userData.user_id);
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
let userIp = payload.get().remote_addr.split(':')[0]
log(uaInfo, userData, userIp, 0)
response.cookies.setRaw(`token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; PartitionKeySite=Main; HttpOnly; Secure`)
// response.headers.set("Location", `${payload.get().query.ref}?secret=${encryptData(token)}`)
// response.status.movedPermanently()
return okMsg(jwt_parse(token))
}
function log(uaInfo, USER_INFO, addr, status) {
if (addr == null) {
return
}
// 记录登录日志
SQL.push(DB_NAME, 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
ex.suid().base58(),
USER_INFO.user_id,
USER_INFO.username,
status,
addr,
'未知',
uaInfo.browser,
uaInfo.os,
uaInfo.deviceModel,
payload.get().headers['User-Agent'][0],
);
}

View File

@@ -0,0 +1,34 @@
// 如果带token请求则为绑定如果不带则为新用户扫码
// 取 Authorization - Bearer 后面的Token值
let userToken = payload.get().headers["Authorization"] || payload.get().query["token"] || payload.get().headers["X-Token"] || (payload.get().cookies ? payload.get().cookie["token"] : null);
if (!userToken) {
// 通过openid查询用户信息
let ses = SQL.query('system_sql', 'SELECT * FROM sys_users WHERE user_id = (SELECT user_id FROM sys_users_oauth WHERE open_id = ?)', res.openid);
if (ses && ses.length) {
const userData = ses[0]
let token = jwt_issue(userData.user_id, userData.nickname);
response.headers.set('Authorization', 'Bearer ' + token)
return okMsg({ expire: sevenDays, token: token, userId: userData.user_id });
}
// SYSTEM_CONFIG.find(item => item.name === 'oauth_safe_secret').value
const chiper = crypto.aes.encrypt('ECB', 'xMagic-oAuth.Secret.1', JSON.stringify(res))
return okMsg({ create: true, token: chiper });
}
// 判断userToken是否是数组
if (Array.isArray(userToken)) {
userToken = userToken[0]
}
// 删除 Bearer 部分
userToken = userToken.replace("Bearer ", "").replace(/\s/g, "");
let tokenInfo = jwt_parse(userToken) // 这里需要密钥进行验证
if (!tokenInfo) {
return errMsg(511, "身份验证失败");
}
// 判断tokenInfo.exp 是否过期
if (tokenInfo.exp < Date.now() / 1000) {
return errMsg(511, "身份凭证已过期");
}

View File

@@ -0,0 +1,94 @@
function jwt_issue(userId, userName) {
// flags: HS256:S RS256:I ES256:F
// header: eyJhbGciOiJ{$flag}UzI1NiIsInR5cCI6IkpXVCJ9. let algo = 'ES256'
let alg
let algo
let typ = 'Uy' // JWS
switch (SYSTEM_CONFIG.find(item => item.name === 'system_jwt_algo').value) {
case 'HS256': // HMAC
alg = 'I'
typ = 'VC' // JWT
break
case 'RS256': // RSA
alg = 'S'
algo = crypto.rsa
break
case 'SS256': // SM2
alg = 'T'
algo = crypto.sm2
break
case 'ES256': // ED25519
alg = 'F'
algo = crypto.ed25519
break
default:
throw new Error('alg not support')
}
// header
let header = `eyJhbGciOiJ${alg}UzI1NiIsInR5cCI6IkpX${typ}J9`
// payload
let payload = encoding.from(JSON.stringify({
uid: userId,
unm: userName,
iss: 'https://l-l.cn',
sub: 'xMagic',
jti: ex.suid().base58(),
nbf: parseInt(new Date().getTime() / 1000),
iat: parseInt(new Date().getTime() / 1000),
exp: parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000),
})).toBase64.url.encoding(true).toString()
// signature
// let pair = algo.new()
// console.log(`{publicKey:'${pair.publicKey}',\nprivateKey:\n'${pair.privateKey}'}`)
let signature = algo.sign(SYSTEM_CONFIG.find(item => item.name === 'system_jwt_private').value, payload).toBase64.url.encoding(true).toString()
let token = `${header}.${payload}.${signature}`
return token
// console.log(`token:\n${token}`)
// let data = token.split('.')[1]
// let code = token.split('.')[2]
// console.log(`code:\n${code}`)
// let isVerfied = algo.verify(pair.publicKey, data, encoding.from(code).toHEX.decoding().Bytes)
// console.log(`isVerified:\n${isVerfied}`)
// os.exit(0)
}
function jwt_parse(token) {
if (!token) return;
let data = token.split('.')[1]
let code = token.split('.')[2]
let algo
switch (SYSTEM_CONFIG.find(item => item.name === 'system_jwt_algo').value) {
case 'HS256': // HMAC
algo = crypto.rsa
break
case 'RS256': // RSA
algo = crypto.rsa
break
case 'SS256': // SM2
algo = crypto.sm2
break
case 'ES256': // ED25519
algo = crypto.ed25519
break
default:
throw new Error('alg not support')
}
let isVerify = algo.verify(SYSTEM_CONFIG.find(item => item.name === 'system_jwt_public').value, data, encoding.from(code).toBase64.url.decoding(true).Bytes)
if (!isVerify) {
return undefined
}
// console.log(data)
data = encoding.from(data).toBase64.url.decoding(true).toObject()
return data
}

View File

@@ -0,0 +1,129 @@
// ------ 用户登录 --------
cache.new('link-auth')
'use api/lib/protect.js'
function main() {
response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate") // 禁用缓存
switch (payload.get().method) {
case 'GET':
return auth()
case 'POST':
return login()
default:
response.status.notFound()
return `404 page not found`
}
}
function login() {
const username = payload.get().path.split('/').pop()
// 设置登录缓存60秒内仅允许请求一次
// if (cache.get('link-auth', username)) {
// return errMsg(511, '请勿重复请求');
// }
let loginType = 'username'
// 手机号
if (username.match(/^\d+$/)) {
loginType = 'phone'
}
// 身份证号
else if (username.match(/^\d{17}[\dXx]$/)) {
loginType = 'idno'
}
// 邮箱
else if (username.match(/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/)) {
loginType = 'email'
}
// 查找用户表
const res = SQL.query('system_sql', `SELECT * FROM sys_users WHERE ${loginType} = ?`, username);
if (res && res.length) {
const userData = res[0];
userData.ip = payload.get().remote_addr.split(':')[0]
const id = ex.suid().base58();
cache.set('link-auth', id, userData, 180 * 1000)
// let sendId = encoding.from(id).toBase58.encoding().toString()
// 如果存在Origin头则优先使用Origin头中的域名否则使用Referer头中的域名。
let sid = runtime.call('api/utils/sortUrl/function.js', `https://x.p-q.co/app/auth/confirm.html?code=${id}`).short_url.sid
let msg = `【仓湖】您正在进行账户登陆操作,请通过 dx5.cn/${sid} 完成身份验证,若非您本人操作,请勿点击并警惕账户安全风险。`
loginType = 'sms'
let sendScript = 'api/utils/sms/send.js'
let hasDingAccount = runtime.call('api/utils/yskt/checkPhone.js', userData.phone)
if (hasDingAccount) {
loginType = 'ding'
sendScript = 'api/utils/yskt/sendMsg.js'
msg = msg.replace('dx5.cn', 'https://dx5.cn')
}
// 发送短信
runtime.exec(sendScript, userData.phone, msg, userData.nickname)
// console.log('send:', id)
const code = encryptData(id)
return okMsg({ code: code, type: loginType, needVerify: !userData.idno });
}
return errMsg(511, '用户不存在');
}
function auth() {
if (!payload.get().headers['User-Agent']) {
return errMsg(413, '参数错误');
}
let uaInfo = ex.parseUserAgent(payload.get().headers['User-Agent'][0]);
let id = payload.get().path.split('/').pop()
const userData = cache.get('link-auth', id)
if (!userData) {
return errMsg(511, '会话过期');
}
// 根据角色id获取key
// const roleRes = SQL.query(DB_NAME, 'SELECT role_key FROM sys_roles WHERE role_id = ?', userData.role_id);
// 生成Token / 7天后过期
const sevenDays = parseInt((new Date().getTime() + 1000 * 60 * 60 * 24 * 7) / 1000);
let token = jwt_issue(userData.user_id, userData.nickname);
// 更新最后登录时间
SQL.push('system_sql', 'UPDATE sys_users SET last_login_time = NOW(),last_login_ip = ? WHERE user_id =?', userData.ip, userData.user_id);
log(uaInfo, userData, payload.get().remote_addr.split(':')[0], 0)
// response.headers.set('Authorization', 'Bearer ' + token)
// let sessionToken = `token=${token}; Path=/; Max-Age=604800; SameSite=Strict; CrossSite=Strict; Priority=High; Partitioned; HttpOnly; Secure`
// response.cookies.setRaw(sessionToken)
let result = { expire: sevenDays, secret: encryptData(token), userId: userData.user_id, sessionId: encryptData(id), needVerify: !userData.idno }
// let result = { expire: sevenDays, userId: userData.user_id, ip: userData.ip, sessionId: encryptData(id) }
sse.send(result.sessionId, 'login', JSON.stringify(result))
// cache.set('link-auth', id, result, 180 * 1000)
return okMsg(true);
}
function log(uaInfo, USER_INFO, addr, status) {
if (addr == null) {
return
}
// 记录登录日志
SQL.push(DB_NAME, 'INSERT INTO log_logins (info_id, user_id, username, status, ipaddr, login_location, browser, os, platform, ua) VALUES (?,?,?,?,?,?,?,?,?,?)',
ex.suid().base58(),
USER_INFO.user_id,
USER_INFO.username,
status,
addr,
'未知',
uaInfo.browser,
uaInfo.os,
uaInfo.deviceModel,
payload.get().headers['User-Agent'][0],
);
}

Some files were not shown because too many files have changed in this diff Show More