Compare commits

..
25 Commits
Author SHA1 Message Date
whyour 325482cbb1 更新版本 v2.13.4 2022-07-16 12:31:50 +08:00
whyour 45e02a55c3 修改npm镜像源 2022-07-16 12:25:14 +08:00
whyour 6b8ef75b51 修复获取环境变量详情 2022-07-16 12:16:33 +08:00
whyour f2535ece76 移除内置token判断 2022-07-11 21:21:35 +08:00
whyour b2afa65cb1 任务详情编辑脚本支持全屏 2022-07-04 22:44:51 +08:00
whyour 3a62cc37ff 环境变量增加导出功能 2022-07-04 22:24:50 +08:00
whyour 93c69826ad 脚本管理按文件类型排序 2022-06-26 22:25:21 +08:00
whyour 70727d0e26 修复mac和linux兼容性 2022-06-26 18:59:39 +08:00
whyour 2578aae0ba 修复手动停止任务耗时日志 2022-06-19 14:32:09 +08:00
whyour 616e56ea95 修复系统token验证 2022-06-18 19:24:56 +08:00
whyour 96ae20de68 修复系统token权限 2022-06-18 19:18:14 +08:00
whyour 6975ff4aa1 修复repo命令 2022-06-18 19:03:50 +08:00
whyour 1d8a1e9c4a 修复repo命令 2022-06-18 19:00:22 +08:00
whyour 2719280cdc 修改结束message 2022-06-18 12:45:38 +08:00
whyour 575e41172f 修改日期打印 2022-06-18 12:43:23 +08:00
whyour bd50b1e976 Merge branch 'develop' of github.com:whyour/qinglong into develop 2022-06-18 12:30:11 +08:00
whyour dfb2572b4f 修复node kill进程逻辑 2022-06-18 12:30:02 +08:00
二毛andGitHub 7746b18023 区分原生termux和termux中安装的Linux发行版 (#1498)
检测termux时只看环境变量,以便于区分原生termux和termux中安装的Linux发行版
测试发现${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT}在termux中安装的Linux发行版中仍旧存在,但是发行版中应该不用设置软连接吧
2022-06-14 22:55:49 +08:00
whyour 05c738c7c6 修改系统内部获取token方式 2022-06-14 22:43:18 +08:00
whyour e561e12356 修改更新定时任务参数验证 2022-06-12 21:12:01 +08:00
whyour 595b28ba62 移除notify软链命令 2022-06-10 21:23:43 +08:00
whyour 366caed74e 修复根目录环境变量 2022-06-09 16:44:58 +08:00
whyour 2e26a8c24e 修复shell本地开发兼容性 2022-06-09 12:10:41 +08:00
whyour c09c375066 修复无法更新脚本内容为空 2022-06-08 23:57:22 +08:00
whyour 4f93905507 修改默认pip源 2022-06-08 22:54:44 +08:00
28 changed files with 375 additions and 138 deletions
+3 -3
View File
@@ -7,6 +7,6 @@ LOG_LEVEL='debug'
SECRET='whyour' SECRET='whyour'
QINIU_AK = '' QINIU_AK=''
QINIU_SK = '' QINIU_SK=''
QINIU_SCOPE = '' QINIU_SCOPE=''
+1 -1
View File
@@ -1,2 +1,2 @@
sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli sentrycli_cdnurl=https://npmmirror.com/mirrors/sentry-cli/
strict-peer-dependencies=false strict-peer-dependencies=false
+7 -7
View File
@@ -182,9 +182,9 @@ export default (app: Router) => {
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
labels: Joi.array().optional().allow(null), labels: Joi.array().optional().allow(null),
command: Joi.string().optional(), command: Joi.string().required(),
schedule: Joi.string().optional(), schedule: Joi.string().required(),
name: Joi.string().optional(), name: Joi.string().optional().allow(null),
id: Joi.number().required(), id: Joi.number().required(),
}), }),
}), }),
@@ -297,10 +297,10 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
ids: Joi.array().items(Joi.number().required()), ids: Joi.array().items(Joi.number().required()),
status: Joi.string().required(), status: Joi.string().required(),
pid: Joi.string().optional(), pid: Joi.string().optional().allow(null),
log_path: Joi.string().optional(), log_path: Joi.string().optional().allow(null),
last_running_time: Joi.number().optional(), last_running_time: Joi.number().optional().allow(null),
last_execution_time: Joi.number().optional(), last_execution_time: Joi.number().optional().allow(null),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
+1 -1
View File
@@ -170,7 +170,7 @@ export default (app: Router) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const envService = Container.get(EnvService); const envService = Container.get(EnvService);
const data = await envService.getDb(req.params.id); const data = await envService.getDb({ id: req.params.id });
return res.send({ code: 200, data }); return res.send({ code: 200, data });
} catch (e) { } catch (e) {
return next(e); return next(e);
+5 -1
View File
@@ -6,6 +6,7 @@ import config from '../config';
import { getFileContentByName, readDirs } from '../config/util'; import { getFileContentByName, readDirs } from '../config/util';
import { join } from 'path'; import { join } from 'path';
const route = Router(); const route = Router();
const blacklist = ['.tmp'];
export default (app: Router) => { export default (app: Router) => {
app.use('/logs', route); app.use('/logs', route);
@@ -13,7 +14,7 @@ export default (app: Router) => {
route.get('/', async (req: Request, res: Response, next: NextFunction) => { route.get('/', async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
const result = readDirs(config.logPath, config.logPath); const result = readDirs(config.logPath, config.logPath, blacklist);
res.send({ res.send({
code: 200, code: 200,
data: result, data: result,
@@ -29,6 +30,9 @@ export default (app: Router) => {
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger'); const logger: Logger = Container.get('logger');
try { try {
if (blacklist.includes(req.path)) {
return res.send({ code: 403, message: '暂无权限' });
}
const filePath = join( const filePath = join(
config.logPath, config.logPath,
(req.query.path || '') as string, (req.query.path || '') as string,
+1 -1
View File
@@ -25,7 +25,7 @@ export default (app: Router) => {
'/apps', '/apps',
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
name: Joi.string().optional().allow(''), name: Joi.string().optional().allow('').disallow('system'),
scopes: Joi.array().items(Joi.string().required()), scopes: Joi.array().items(Joi.string().required()),
}), }),
}), }),
+1 -1
View File
@@ -133,7 +133,7 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
filename: Joi.string().required(), filename: Joi.string().required(),
path: Joi.string().optional().allow(''), path: Joi.string().optional().allow(''),
content: Joi.string().required(), content: Joi.string().required().allow(''),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
+1
View File
@@ -69,6 +69,7 @@ export default {
'cookie.sh', 'cookie.sh',
'crontab.list', 'crontab.list',
'env.sh', 'env.sh',
'token.json',
], ],
writePathList: [configPath, scriptPath], writePathList: [configPath, scriptPath],
bakPath, bakPath,
+25 -2
View File
@@ -2,6 +2,7 @@ import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import got from 'got'; import got from 'got';
import iconv from 'iconv-lite'; import iconv from 'iconv-lite';
import { exec } from 'child_process';
export function getFileContentByName(fileName: string) { export function getFileContentByName(fileName: string) {
if (fs.existsSync(fileName)) { if (fs.existsSync(fileName)) {
@@ -277,6 +278,11 @@ export async function concurrentRun(
return replyList; return replyList;
} }
enum FileType {
'directory',
'file',
}
export function readDirs( export function readDirs(
dir: string, dir: string,
baseDir: string = '', baseDir: string = '',
@@ -297,7 +303,10 @@ export function readDirs(
type: 'directory', type: 'directory',
disabled: true, disabled: true,
parent: relativePath, parent: relativePath,
children: readDirs(subPath, baseDir), children: readDirs(subPath, baseDir).sort(
(a: any, b: any) =>
(FileType as any)[a.type] - (FileType as any)[b.type],
),
}; };
} }
return { return {
@@ -307,7 +316,9 @@ export function readDirs(
parent: relativePath, parent: relativePath,
}; };
}); });
return result; return result.sort(
(a: any, b: any) => (FileType as any)[a.type] - (FileType as any)[b.type],
);
} }
export function readDir( export function readDir(
@@ -332,3 +343,15 @@ export function readDir(
}); });
return result; return result;
} }
export function promiseExec(command: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(
command,
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' },
(err, stdout, stderr) => {
resolve(stdout || stderr || JSON.stringify(err));
},
);
});
}
+26
View File
@@ -2,6 +2,7 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import chokidar from 'chokidar'; import chokidar from 'chokidar';
import config from '../config/index'; import config from '../config/index';
import { promiseExec } from '../config/util';
function linkToNodeModule(src: string, dst?: string) { function linkToNodeModule(src: string, dst?: string) {
const target = path.join(config.rootPath, 'node_modules', dst || src); const target = path.join(config.rootPath, 'node_modules', dst || src);
@@ -16,7 +17,32 @@ function linkToNodeModule(src: string, dst?: string) {
}); });
} }
async function linkCommand() {
const commandPath = await promiseExec('which node');
const commandDir = path.dirname(commandPath);
const linkShell = [
{
src: 'update.sh',
dest: 'ql',
},
{
src: 'task.sh',
dest: 'task',
},
];
for (const link of linkShell) {
const source = path.join(config.rootPath, 'shell', link.src);
const target = path.join(commandDir, link.dest);
if (fs.existsSync(target)) {
fs.unlinkSync(target);
}
fs.symlink(source, target, (err) => {});
}
}
export default async (src: string = 'deps') => { export default async (src: string = 'deps') => {
await linkCommand();
linkToNodeModule(src); linkToNodeModule(src);
const source = path.join(config.rootPath, src); const source = path.join(config.rootPath, src);
-7
View File
@@ -80,13 +80,6 @@ export default ({ app }: { app: Application }) => {
) { ) {
return next(); return next();
} }
const remoteAddress = req.socket.remoteAddress;
if (
remoteAddress === '::ffff:127.0.0.1' &&
originPath === '/api/crons/status'
) {
return next();
}
const data = fs.readFileSync(config.authConfigFile, 'utf8'); const data = fs.readFileSync(config.authConfigFile, 'utf8');
if (data) { if (data) {
+8
View File
@@ -64,5 +64,13 @@ export default async () => {
dotenv.config({ path: confFile }); dotenv.config({ path: confFile });
// 声明QL_DIR环境变量
let qlHomePath = path.join(__dirname, '../../');
// 生产环境
if (qlHomePath.endsWith('/static/')) {
qlHomePath = path.join(qlHomePath, '../');
}
process.env.QL_DIR = qlHomePath;
Logger.info('✌️ Init file down'); Logger.info('✌️ Init file down');
}; };
+13 -3
View File
@@ -10,6 +10,7 @@ import { promises, existsSync } from 'fs';
import { promisify } from 'util'; import { promisify } from 'util';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
import path from 'path'; import path from 'path';
import dayjs from 'dayjs';
@Service() @Service()
export default class CronService { export default class CronService {
@@ -197,13 +198,21 @@ export default class CronService {
const err = await this.killTask(doc.command); const err = await this.killTask(doc.command);
const absolutePath = path.resolve(config.logPath, `${doc.log_path}`); const absolutePath = path.resolve(config.logPath, `${doc.log_path}`);
const logFileExist = doc.log_path && (await fileExist(absolutePath)); const logFileExist = doc.log_path && (await fileExist(absolutePath));
const endTime = dayjs();
const diffTimeStr = doc.last_execution_time
? `,耗时 ${endTime.diff(
dayjs(doc.last_execution_time * 1000),
'second',
)}`
: '';
if (logFileExist) { if (logFileExist) {
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';
fs.appendFileSync( fs.appendFileSync(
`${absolutePath}`, `${absolutePath}`,
`${str}\n## 执行结束... ${new Date() `${str}\n## 执行结束... ${endTime.format(
.toLocaleString('zh', { hour12: false }) 'YYYY-MM-DD HH:mm:ss',
.replace(' 24:', ' 00:')} `, )}${diffTimeStr}`,
); );
} }
} }
@@ -228,6 +237,7 @@ export default class CronService {
const killLogs = []; const killLogs = [];
if (pids && pids.length > 0) { if (pids && pids.length > 0) {
// node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个 // node 执行脚本时还会有10个子进程,但是ps -ef中不存在,所以截取前三个
pids = pids.slice(0, 3);
for (const id of pids) { for (const id of pids) {
const c = `kill -9 ${id.slice(1)}`; const c = `kill -9 ${id.slice(1)}`;
try { try {
+14 -20
View File
@@ -14,6 +14,7 @@ import { spawn } from 'child_process';
import SockService from './sock'; import SockService from './sock';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
import { concurrentRun } from '../config/util'; import { concurrentRun } from '../config/util';
import dayjs from 'dayjs';
@Service() @Service()
export default class DependenceService { export default class DependenceService {
@@ -161,20 +162,17 @@ export default class DependenceService {
)[dependencies[0].type as any]; )[dependencies[0].type as any];
const actionText = isInstall ? '安装' : '删除'; const actionText = isInstall ? '安装' : '删除';
const depIds = dependencies.map((x) => x.id) as number[]; const depIds = dependencies.map((x) => x.id) as number[];
const startTime = Date.now(); const startTime = dayjs();
const message = `开始${actionText}依赖 ${depNames},开始时间 ${startTime.format(
'YYYY-MM-DD HH:mm:ss',
)}\n\n`;
this.sockService.sendMessage({ this.sockService.sendMessage({
type: socketMessageType, type: socketMessageType,
message: `开始${actionText}依赖 ${depNames},开始时间 ${new Date( message,
startTime,
).toLocaleString()}\n\n`,
references: depIds, references: depIds,
}); });
await this.updateLog( await this.updateLog(depIds, message);
depIds,
`开始${actionText}依赖 ${depNames},开始时间 ${new Date(
startTime,
).toLocaleString()}\n\n`,
);
const cp = spawn(`${depRunCommand} ${depNames}`, { shell: '/bin/bash' }); const cp = spawn(`${depRunCommand} ${depNames}`, { shell: '/bin/bash' });
@@ -207,23 +205,19 @@ export default class DependenceService {
}); });
cp.on('close', async (code) => { cp.on('close', async (code) => {
const endTime = Date.now(); const endTime = dayjs();
const isSucceed = code === 0; const isSucceed = code === 0;
const resultText = isSucceed ? '成功' : '失败'; const resultText = isSucceed ? '成功' : '失败';
const message = `\n依赖${actionText}${resultText},结束时间 ${endTime.format(
'YYYY-MM-DD HH:mm:ss',
)},耗时 ${endTime.diff(startTime, 'second')}`;
this.sockService.sendMessage({ this.sockService.sendMessage({
type: socketMessageType, type: socketMessageType,
message: `\n依赖${actionText}${resultText},结束时间 ${new Date( message,
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
references: depIds, references: depIds,
}); });
await this.updateLog( await this.updateLog(depIds, message);
depIds,
`\n依赖${actionText}${resultText},结束时间 ${new Date(
endTime,
).toLocaleString()},耗时 ${(endTime - startTime) / 1000}`,
);
let status = null; let status = null;
if (isSucceed) { if (isSucceed) {
+34 -1
View File
@@ -90,7 +90,9 @@ export default class OpenService {
} }
try { try {
const result = await this.find(condition); const result = await this.find(condition);
return result.map((x) => ({ ...x, tokens: [] })); return result
.filter((x) => x.name !== 'system')
.map((x) => ({ ...x, tokens: [] }));
} catch (error) { } catch (error) {
throw error; throw error;
} }
@@ -142,4 +144,35 @@ export default class OpenService {
return { code: 400, message: 'client_id或client_seret有误' }; return { code: 400, message: 'client_id或client_seret有误' };
} }
} }
public async findSystemToken(): Promise<{
value: string;
expiration: number;
}> {
let systemApp = (await AppModel.findOne({
where: { name: 'system' },
})) as App;
if (!systemApp) {
systemApp = await this.create({
name: 'system',
scopes: ['crons', 'system'],
} as App);
}
const nowTime = Math.round(Date.now() / 1000);
let token;
if (
!systemApp.tokens ||
!systemApp.tokens.length ||
nowTime > [...systemApp.tokens].pop()!.expiration
) {
const authToken = await this.authToken({
client_id: systemApp.client_id,
client_secret: systemApp.client_secret,
});
token = authToken.data;
} else {
token = [...systemApp.tokens].pop();
}
return token;
}
} }
+3 -3
View File
@@ -28,6 +28,7 @@ import ScheduleService, { TaskCallbacks } from './schedule';
import { SimpleIntervalSchedule } from 'toad-scheduler'; import { SimpleIntervalSchedule } from 'toad-scheduler';
import SockService from './sock'; import SockService from './sock';
import SshKeyService from './sshKey'; import SshKeyService from './sshKey';
import dayjs from 'dayjs';
@Service() @Service()
export default class SubscriptionService { export default class SubscriptionService {
@@ -371,11 +372,10 @@ export default class SubscriptionService {
const err = await this.killTask(command); const err = await this.killTask(command);
const absolutePath = await this.handleLogPath(doc.log_path as string); const absolutePath = await this.handleLogPath(doc.log_path as string);
const str = err ? `\n${err}` : ''; const str = err ? `\n${err}` : '';
fs.appendFileSync( fs.appendFileSync(
`${absolutePath}`, `${absolutePath}`,
`${str}\n## 执行结束... ${new Date() `${str}\n## 执行结束... ${dayjs().format('YYYY-MM-DD HH:mm:ss')} `,
.toLocaleString('zh', { hour12: false })
.replace(' 24:', ' 00:')} `,
); );
} }
+3 -3
View File
@@ -13,7 +13,7 @@ import { Request } from 'express';
import ScheduleService from './schedule'; import ScheduleService from './schedule';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import SockService from './sock'; import SockService from './sock';
import got from 'got'; import dayjs from 'dayjs';
@Service() @Service()
export default class UserService { export default class UserService {
@@ -111,7 +111,7 @@ export default class UserService {
}); });
await this.notificationService.notify( await this.notificationService.notify(
'登录通知', '登录通知',
`你于${new Date(timestamp).toLocaleString()}${address} ${ `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}${address} ${
req.platform req.platform
}端 登录成功,ip地址 ${ip}`, }端 登录成功,ip地址 ${ip}`,
); );
@@ -140,7 +140,7 @@ export default class UserService {
}); });
await this.notificationService.notify( await this.notificationService.notify(
'登录通知', '登录通知',
`你于${new Date(timestamp).toLocaleString()}${address} ${ `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}${address} ${
req.platform req.platform
}端 登录失败,ip地址 ${ip}`, }端 登录失败,ip地址 ${ip}`,
); );
+1 -1
View File
@@ -39,7 +39,7 @@ AutoStartBot=""
BotRepoUrl="" BotRepoUrl=""
## 安装bot依赖时指定pip源,默认使用清华源,如不需要源,设置此参数为空 ## 安装bot依赖时指定pip源,默认使用清华源,如不需要源,设置此参数为空
PipMirror="https://pypi.tuna.tsinghua.edu.cn/simple" PipMirror="https://pypi.doubanio.com/simple/"
## 通知环境变量 ## 通知环境变量
## 1. Server酱 ## 1. Server酱
+8 -26
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
get_token() { get_token() {
token=$(cat $file_auth_user | jq -r .token) token=$(ts-node-transpile-only "$dir_shell/token.ts")
} }
add_cron_api() { add_cron_api() {
@@ -17,7 +17,7 @@ add_cron_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
-H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" \
@@ -52,7 +52,7 @@ update_cron_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -84,7 +84,7 @@ update_cron_command_api() {
fi fi
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -109,7 +109,7 @@ del_cron_api() {
local ids=$1 local ids=$1
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons?t=$currentTimeStamp" \
-X 'DELETE' \ -X 'DELETE' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -130,24 +130,6 @@ del_cron_api() {
fi fi
} }
get_user_info() {
local currentTimeStamp=$(date +%s)
local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/user?t=$currentTimeStamp" \
-H 'Accept: */*' \
-H "Authorization: Bearer $token" \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36' \
-H 'Referer: http://0.0.0.0:5700/crontab' \
-H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7' \
--compressed
)
code=$(echo $api | jq -r .code)
if [[ $code != 200 ]]; then
echo -e "请先登录!"
exit 0
fi
}
update_cron() { update_cron() {
local ids="$1" local ids="$1"
local status="$2" local status="$2"
@@ -157,7 +139,7 @@ update_cron() {
local runningTime="${6:-0}" local runningTime="${6:-0}"
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/crons/status?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/crons/status?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
@@ -172,7 +154,7 @@ update_cron() {
code=$(echo $api | jq -r .code) code=$(echo $api | jq -r .code)
message=$(echo $api | jq -r .message) message=$(echo $api | jq -r .message)
if [[ $code != 200 ]]; then if [[ $code != 200 ]]; then
echo -e "\n## 更新任务状态失败(${message})\n" >> $log_path echo -e "\n## 更新任务状态失败(${message})\n" >> $dir_log/$log_path
fi fi
} }
@@ -181,7 +163,7 @@ notify_api() {
local content=$2 local content=$2
local currentTimeStamp=$(date +%s) local currentTimeStamp=$(date +%s)
local api=$( local api=$(
curl -s --noproxy "*" "http://0.0.0.0:5600/api/system/notify?t=$currentTimeStamp" \ curl -s --noproxy "*" "http://0.0.0.0:5600/open/system/notify?t=$currentTimeStamp" \
-X 'PUT' \ -X 'PUT' \
-H "Accept: application/json" \ -H "Accept: application/json" \
-H "Authorization: Bearer $token" \ -H "Authorization: Bearer $token" \
+39 -7
View File
@@ -14,7 +14,6 @@ dir_log=$dir_data/log
dir_db=$dir_data/db dir_db=$dir_data/db
dir_dep=$dir_data/deps dir_dep=$dir_data/deps
dir_list_tmp=$dir_log/.tmp dir_list_tmp=$dir_log/.tmp
dir_code=$dir_log/code
dir_update_log=$dir_log/update dir_update_log=$dir_log/update
ql_static_repo=$dir_repo/static ql_static_repo=$dir_repo/static
@@ -52,12 +51,10 @@ list_own_drop=$dir_list_tmp/own_drop.list
link_name=( link_name=(
task task
ql ql
notify
) )
original_name=( original_name=(
task.sh task.sh
update.sh update.sh
notify.sh
) )
init_env() { init_env() {
@@ -101,7 +98,7 @@ make_dir() {
} }
detect_termux() { detect_termux() {
if [[ ${ANDROID_RUNTIME_ROOT}${ANDROID_ROOT} ]] || [[ $PATH == *com.termux* ]]; then if [[ $PATH == *com.termux* ]]; then
is_termux=1 is_termux=1
else else
is_termux=0 is_termux=0
@@ -248,11 +245,11 @@ fix_config() {
npm_install_sub() { npm_install_sub() {
set_proxy set_proxy
if [ $is_termux -eq 1 ]; then if [ $is_termux -eq 1 ]; then
npm install --production --no-bin-links --registry=https://registry.npm.taobao.org || npm install --production --no-bin-links npm install --production --no-bin-links --registry=https://registry.npmmirror.com || npm install --production --no-bin-links
elif ! type pnpm &>/dev/null; then elif ! type pnpm &>/dev/null; then
npm install --production --registry=https://registry.npm.taobao.org || npm install --production npm install --production --registry=https://registry.npmmirror.com || npm install --production
else else
pnpm install --loglevel error --production --registry=https://registry.npm.taobao.org || pnpm install --production --loglevel error pnpm install --loglevel error --production --registry=https://registry.npmmirror.com || pnpm install --production --loglevel error
fi fi
unset_proxy unset_proxy
} }
@@ -380,6 +377,41 @@ reload_pm2() {
pm2 start $dir_static/build/public.js -n public --source-map-support --time &>/dev/null pm2 start $dir_static/build/public.js -n public --source-map-support --time &>/dev/null
} }
diff_time() {
local format="$1"
local begin_time="$2"
local end_time="$3"
if [[ $is_macos -eq 1 ]]; then
diff_time=$(($(date -j -f "$format" "$end_time" +%s) - $(date -j -f "$format" "$begin_time" +%s)))
else
diff_time=$(($(date +%s -d "$end_time") - $(date +%s -d "$begin_time")))
fi
echo "$diff_time"
}
format_time() {
local format="$1"
local time="$2"
if [[ $is_macos -eq 1 ]]; then
echo $(date -j -f "$format" "$time" "+%Y-%m-%d %H:%M:%S")
else
echo $(date -d "$time" "+%Y-%m-%d %H:%M:%S")
fi
}
format_timestamp() {
local format="$1"
local time="$2"
if [[ $is_macos -eq 1 ]]; then
echo $(date -j -f "$format" "$time" "+%s")
else
echo $(date -d "$time" "+%s")
fi
}
init_env init_env
detect_termux detect_termux
detect_macos detect_macos
+31 -25
View File
@@ -84,8 +84,8 @@ run_normal() {
fi fi
fi fi
local time=$(date) local time=$(date "+%Y-%m-%d-%H-%M-%S")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time="$time"
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -99,12 +99,12 @@ run_normal() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "%Y-%m-%d-%H-%M-%S" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "%Y-%m-%d-%H-%M-%S" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -118,12 +118,13 @@ run_normal() {
cd ${relative_path} cd ${relative_path}
file_param=${file_param/$relative_path\//} file_param=${file_param/$relative_path\//}
fi fi
eval timeout -k 10s $command_timeout_time $which_program $file_param $cmd
eval $timeoutCmd $which_program $file_param $cmd
eval . $file_task_after "$@" $cmd eval . $file_task_after "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
local end_timestamp=$(date "+%s") local end_timestamp=$(date "+%s")
local diff_time=$(( $end_timestamp - $begin_timestamp )) local diff_time=$(expr $end_timestamp - $begin_timestamp)
[[ $id ]] && update_cron "\"$id\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time" [[ $id ]] && update_cron "\"$id\"" "1" "" "$log_path" "$begin_timestamp" "$diff_time"
eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time" $cmd eval echo -e "\\\n\#\# 执行结束... $end_time 耗时 $diff_time" $cmd
} }
@@ -154,8 +155,8 @@ run_concurrent() {
[[ ! -z $cookieStr ]] && export ${env_param}=${cookieStr} [[ ! -z $cookieStr ]] && export ${env_param}=${cookieStr}
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+%Y-%m-%d-%H-%M-%S")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time="$time"
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -169,12 +170,12 @@ run_concurrent() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "%Y-%m-%d-%H-%M-%S" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "%Y-%m-%d-%H-%M-%S" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -196,7 +197,7 @@ run_concurrent() {
for i in "${!array[@]}"; do for i in "${!array[@]}"; do
export ${env_param}=${array[i]} export ${env_param}=${array[i]}
single_log_path="$dir_log/$log_dir/${single_log_time}_$((i + 1)).log" single_log_path="$dir_log/$log_dir/${single_log_time}_$((i + 1)).log"
timeout -k 10s $command_timeout_time $which_program $file_param &>$single_log_path & eval $timeoutCmd $which_program $file_param &>$single_log_path &
done done
wait wait
@@ -224,8 +225,8 @@ run_designated() {
fi fi
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+%Y-%m-%d-%H-%M-%S")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time="$time"
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -239,12 +240,12 @@ run_designated() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "%Y-%m-%d-%H-%M-%S" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "%Y-%m-%d-%H-%M-%S" "$time")
local envs=$(eval echo "\$${env_param}") local envs=$(eval echo "\$${env_param}")
local array=($(echo $envs | sed 's/&/ /g')) local array=($(echo $envs | sed 's/&/ /g'))
@@ -274,7 +275,7 @@ run_designated() {
cd ${relative_path} cd ${relative_path}
file_param=${file_param/$relative_path\//} file_param=${file_param/$relative_path\//}
fi fi
eval timeout -k 10s $command_timeout_time $which_program $file_param $cmd eval $timeoutCmd $which_program $file_param $cmd
eval . $file_task_after "$@" $cmd eval . $file_task_after "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
@@ -288,8 +289,8 @@ run_designated() {
run_else() { run_else() {
local file_param="$1" local file_param="$1"
define_program "$file_param" define_program "$file_param"
local time=$(date) local time=$(date "+%Y-%m-%d-%H-%M-%S")
log_time=$(date -d "$time" "+%Y-%m-%d-%H-%M-%S") log_time="$time"
log_dir_tmp="${file_param##*/}" log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then if [[ $file_param == /* ]]; then
@@ -303,12 +304,12 @@ run_else() {
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}" [[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}" log_dir="${log_dir_tmp%.*}"
log_path="$log_dir/$log_time.log" log_path="$log_dir/$log_time.log"
cmd="&>> $dir_log/$log_path" cmd=">> $dir_log/$log_path 2>&1"
[[ "$show_log" == "true" ]] && cmd="" [[ "$show_log" == "true" ]] && cmd=""
make_dir "$dir_log/$log_dir" make_dir "$dir_log/$log_dir"
local begin_time=$(date -d "$time" "+%Y-%m-%d %H:%M:%S") local begin_time=$(format_time "%Y-%m-%d-%H-%M-%S" "$time")
local begin_timestamp=$(date -d "$time" "+%s") local begin_timestamp=$(format_timestamp "%Y-%m-%d-%H-%M-%S" "$time")
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
[[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd [[ -f $task_error_log_path ]] && eval cat $task_error_log_path $cmd
@@ -325,7 +326,7 @@ run_else() {
fi fi
shift shift
eval timeout -k 10s $command_timeout_time $which_program "$file_param" "$@" $cmd eval $timeoutCmd $which_program "$file_param" "$@" $cmd
eval . $file_task_after "$file_param" "$@" $cmd eval . $file_task_after "$file_param" "$@" $cmd
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
@@ -348,6 +349,11 @@ main() {
done done
[[ "$show_log" == "true" ]] && shift $(($OPTIND - 1)) [[ "$show_log" == "true" ]] && shift $(($OPTIND - 1))
timeoutCmd=""
if type timeout &>/dev/null; then
timeoutCmd="timeout -k 10s $command_timeout_time "
fi
if [[ $1 == *.js ]] || [[ $1 == *.py ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then if [[ $1 == *.js ]] || [[ $1 == *.py ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then
case $# in case $# in
1) 1)
Executable
+56
View File
@@ -0,0 +1,56 @@
import 'reflect-metadata';
import OpenService from '../back/services/open';
import { Container } from 'typedi';
import LoggerInstance from '../back/loaders/logger';
import fs from 'fs';
import config from '../back/config';
import path from 'path';
const tokenFile = path.join(config.configPath, 'token.json');
async function getToken() {
try {
const data = await readFile();
const nowTime = Math.round(Date.now() / 1000);
if (data.value && data.expiration > nowTime) {
console.log(data.value);
} else {
Container.set('logger', LoggerInstance);
const openService = Container.get(OpenService);
const appToken = await openService.findSystemToken();
console.log(appToken.value);
await writeFile({
value: appToken.value,
expiration: appToken.expiration,
});
}
} catch (error) {
console.log(error);
}
}
async function readFile() {
return new Promise<any>((resolve, reject) => {
fs.readFile(
path.join(config.configPath, 'token.json'),
{ encoding: 'utf8' },
(err, data) => {
if (err) {
resolve({});
} else {
resolve(JSON.parse(data));
}
},
);
});
}
async function writeFile(data: any) {
return new Promise<void>((resolve, reject) => {
fs.writeFile(tokenFile, JSON.stringify(data), { encoding: 'utf8' }, () => {
resolve();
});
});
}
getToken();
+8 -8
View File
@@ -12,14 +12,16 @@ diff_cron() {
local list_task="$2" local list_task="$2"
local list_add="$3" local list_add="$3"
local list_drop="$4" local list_drop="$4"
if [[ -s $list_task ]]; then if [[ -s $list_task ]] && [[ -s $list_scripts ]]; then
grep -vwf $list_task $list_scripts >$list_add grep -vwf $list_task $list_scripts >$list_add
elif [[ ! -s $list_task ]] && [[ -s $list_scripts ]]; then grep -vwf $list_scripts $list_task >$list_drop
fi
if [[ ! -s $list_task ]] && [[ -s $list_scripts ]]; then
cp -f $list_scripts $list_add cp -f $list_scripts $list_add
fi fi
if [[ -s $list_scripts ]]; then
grep -vwf $list_scripts $list_task >$list_drop if [[ ! -s $list_scripts ]] && [[ -s $list_task ]]; then
else
cp -f $list_task $list_drop cp -f $list_task $list_drop
fi fi
} }
@@ -486,7 +488,6 @@ main() {
run_extra_shell >>$log_path run_extra_shell >>$log_path
;; ;;
repo) repo)
get_user_info
get_uniq_path "$p2" "$p6" get_uniq_path "$p2" "$p6"
if [[ -n $p2 ]]; then if [[ -n $p2 ]]; then
update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7" update_repo "$p2" "$p3" "$p4" "$p5" "$p6" "$p7"
@@ -496,7 +497,6 @@ main() {
fi fi
;; ;;
raw) raw)
get_user_info
get_uniq_path "$p2" get_uniq_path "$p2"
if [[ -n $p2 ]]; then if [[ -n $p2 ]]; then
update_raw "$p2" update_raw "$p2"
@@ -538,7 +538,7 @@ main() {
;; ;;
esac esac
local end_time=$(date '+%Y-%m-%d %H:%M:%S') local end_time=$(date '+%Y-%m-%d %H:%M:%S')
local diff_time=$(($(date +%s -d "$end_time") - $(date +%s -d "$begin_time"))) local diff_time=$(diff_time "%Y-%m-%d %H:%M:%S" "$begin_time" "$end_time")
if [[ $p1 != "repo" ]] && [[ $p1 != "raw" ]]; then if [[ $p1 != "repo" ]] && [[ $p1 != "raw" ]]; then
echo -e "\n## 执行结束... $end_time 耗时 $diff_time" >>$log_path echo -e "\n## 执行结束... $end_time 耗时 $diff_time" >>$log_path
cat $log_path cat $log_path
+15 -1
View File
@@ -20,6 +20,7 @@ import {
FileOutlined, FileOutlined,
PlayCircleOutlined, PlayCircleOutlined,
PauseCircleOutlined, PauseCircleOutlined,
FullscreenOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { CrontabStatus } from './index'; import { CrontabStatus } from './index';
import { diffTime } from '@/utils/date'; import { diffTime } from '@/utils/date';
@@ -106,7 +107,8 @@ const CronDetailModal = ({
glyphMargin: false, glyphMargin: false,
wordWrap: 'on', wordWrap: 'on',
}} }}
onMount={(editor) => { onMount={(editor, monaco) => {
console.log(monaco);
editorRef.current = editor; editorRef.current = editor;
}} }}
/> />
@@ -352,6 +354,11 @@ const CronDetailModal = ({
}); });
}; };
const fullscreen = () => {
const editorElement = editorRef.current._domElement as HTMLElement;
editorElement.parentElement?.requestFullscreen();
};
useEffect(() => { useEffect(() => {
if (cron && cron.id) { if (cron && cron.id) {
setCurrentCron(cron); setCurrentCron(cron);
@@ -529,6 +536,7 @@ const CronDetailModal = ({
}} }}
tabBarExtraContent={ tabBarExtraContent={
activeTabKey === 'script' && ( activeTabKey === 'script' && (
<>
<Button <Button
type="primary" type="primary"
style={{ marginRight: 8 }} style={{ marginRight: 8 }}
@@ -536,6 +544,12 @@ const CronDetailModal = ({
> >
</Button> </Button>
<Button
type="primary"
icon={<FullscreenOutlined />}
onClick={fullscreen}
/>
</>
) )
} }
> >
+15 -1
View File
@@ -25,7 +25,7 @@ import EditNameModal from './editNameModal';
import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import './index.less'; import './index.less';
import { getTableScroll } from '@/utils/index'; import { exportJson, getTableScroll } from '@/utils/index';
const { Text, Paragraph } = Typography; const { Text, Paragraph } = Typography;
const { Search } = Input; const { Search } = Input;
@@ -462,6 +462,13 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
}); });
}; };
const exportEnvs = () => {
const envs = value
.filter((x) => selectedRowIds.includes(x.id))
.map((x) => ({ value: x.value, name: x.name, remarks: x.remarks }));
exportJson('env.json', JSON.stringify(envs));
};
const modifyName = () => { const modifyName = () => {
setIsEditNameModalVisible(true); setIsEditNameModalVisible(true);
}; };
@@ -516,6 +523,13 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
> >
</Button> </Button>
<Button
type="primary"
onClick={() => exportEnvs()}
style={{ marginLeft: 8, marginRight: 8 }}
>
</Button>
<Button <Button
type="primary" type="primary"
onClick={() => operateEnvs(0)} onClick={() => operateEnvs(0)}
+1 -1
View File
@@ -48,7 +48,7 @@ const EditScriptNameModal = ({
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success('新建文件成功');
const key = values.path ? `${values.path}/` : ''; const key = values.path ? `${values.path}/` : '';
const filename = file ? file.name : values.filename; const filename = file ? file.name : values.filename;
handleCancel({ handleCancel({
+36
View File
@@ -205,3 +205,39 @@ export function getTableScroll({
let height = document.body.clientHeight - mainTop - extraHeight; let height = document.body.clientHeight - mainTop - extraHeight;
return height; return height;
} }
// 自动触发点击事件
function automaticClick(elment: HTMLElement) {
const ev = document.createEvent('MouseEvents');
ev.initMouseEvent(
'click',
true,
false,
window,
0,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null,
);
elment.dispatchEvent(ev);
}
// 导出文件
export function exportJson(name: string, data: string) {
const urlObject = window.URL || window.webkitURL || window;
const export_blob = new Blob([data]);
const createA = document.createElementNS(
'http://www.w3.org/1999/xhtml',
'a',
) as any;
createA.href = urlObject.createObjectURL(export_blob);
createA.download = name;
automaticClick(createA);
}
+12 -7
View File
@@ -1,9 +1,14 @@
export const version = '2.13.3'; export const version = '2.13.4';
export const changeLogLink = 'https://t.me/jiao_long/303'; export const changeLogLink = 'https://t.me/jiao_long/303';
export const changeLog = `2.13.3 版本说明 export const changeLog = `2.13.4 版本说明
1. 新建脚本支持文件上传 1. 增加系统token,防止未登录时无法更新仓库和任务状态
2. 修复调试脚本运行,不需要每次保存 2. 脚本管理按照类型排序
3. 修复新建脚本选择嵌套目录 3. 任务详情中编辑脚本支持全屏
4. 修复粘贴订阅,私有仓库字段不更新 4. 环境变量增加导出功能
5. 修复脚本管理、日志管理树 5. 修复无法更新脚本内容为空
6. 修复停止node脚本报错
7. 修改手动停止任务耗时
8. 修复脚本管理、日志管理树
9. 修复获取环境变量详情
10. 区分原生termux和termux中安装的Linux发,感谢 erma0
`; `;