mirror of
https://github.com/whyour/qinglong.git
synced 2026-07-01 04:40:38 +08:00
支持多语言英文
This commit is contained in:
+2
-3
@@ -53,7 +53,7 @@ export default (app: Router) => {
|
||||
body: Joi.object({
|
||||
filename: Joi.string().required(),
|
||||
path: Joi.string().allow(''),
|
||||
type: Joi.string().optional()
|
||||
type: Joi.string().optional(),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
@@ -65,7 +65,7 @@ export default (app: Router) => {
|
||||
};
|
||||
const filePath = join(config.logPath, path, filename);
|
||||
if (type === 'directory') {
|
||||
emptyDir(filePath);
|
||||
emptyDir(filePath);
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
@@ -75,5 +75,4 @@ export default (app: Router) => {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
+2
-3
@@ -35,9 +35,8 @@ export default (app: Router) => {
|
||||
try {
|
||||
const userService = Container.get(UserService);
|
||||
const authInfo = await userService.getUserInfo();
|
||||
const { version, changeLog, changeLogLink, publishTime } = await parseVersion(
|
||||
config.versionFile,
|
||||
);
|
||||
const { version, changeLog, changeLogLink, publishTime } =
|
||||
await parseVersion(config.versionFile);
|
||||
|
||||
let isInitialized = true;
|
||||
if (
|
||||
|
||||
@@ -34,9 +34,11 @@ export function formatCommand(doc: Subscription, url?: string) {
|
||||
if (type === 'file') {
|
||||
command += `raw "${_url}"`;
|
||||
} else {
|
||||
command += `repo "${_url}" "${whitelist || ''}" "${blacklist || ''}" "${dependences || ''
|
||||
}" "${branch || ''}" "${extensions || ''}" "${proxy || ''}" "${isNil(autoAddCron) ? true : Boolean(autoAddCron)
|
||||
}" "${isNil(autoDelCron) ? true : Boolean(autoDelCron)}"`;
|
||||
command += `repo "${_url}" "${whitelist || ''}" "${blacklist || ''}" "${
|
||||
dependences || ''
|
||||
}" "${branch || ''}" "${extensions || ''}" "${proxy || ''}" "${
|
||||
isNil(autoAddCron) ? true : Boolean(autoAddCron)
|
||||
}" "${isNil(autoDelCron) ? true : Boolean(autoDelCron)}"`;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
+5
-3
@@ -39,12 +39,14 @@ export interface LoginLogInfo {
|
||||
address?: string;
|
||||
ip?: string;
|
||||
platform?: string;
|
||||
status?: LoginStatus,
|
||||
status?: LoginStatus;
|
||||
}
|
||||
|
||||
export type AuthModelInfo = SystemConfigInfo & Partial<NotificationInfo> & LoginLogInfo;
|
||||
export type AuthModelInfo = SystemConfigInfo &
|
||||
Partial<NotificationInfo> &
|
||||
LoginLogInfo;
|
||||
|
||||
export interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo { }
|
||||
export interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo {}
|
||||
export const AuthModel = sequelize.define<AuthInstance>('Auth', {
|
||||
ip: DataTypes.STRING,
|
||||
type: DataTypes.STRING,
|
||||
|
||||
@@ -66,7 +66,7 @@ export enum unInstallDependenceCommandTypes {
|
||||
|
||||
export interface DependenceInstance
|
||||
extends Model<Dependence, Dependence>,
|
||||
Dependence { }
|
||||
Dependence {}
|
||||
export const DependenceModel = sequelize.define<DependenceInstance>(
|
||||
'Dependence',
|
||||
{
|
||||
|
||||
@@ -30,15 +30,15 @@ export default async () => {
|
||||
});
|
||||
|
||||
// 初始化更新所有任务状态为空闲
|
||||
await CrontabModel.update(
|
||||
{ status: CrontabStatus.idle },
|
||||
{ where: {} },
|
||||
);
|
||||
await CrontabModel.update({ status: CrontabStatus.idle }, { where: {} });
|
||||
|
||||
// 初始化时安装所有处于安装中,安装成功,安装失败的依赖
|
||||
DependenceModel.findAll({
|
||||
where: {},
|
||||
order: [['type', 'DESC'], ['createdAt', 'DESC']],
|
||||
order: [
|
||||
['type', 'DESC'],
|
||||
['createdAt', 'DESC'],
|
||||
],
|
||||
raw: true,
|
||||
}).then(async (docs) => {
|
||||
await DependenceModel.update(
|
||||
|
||||
@@ -5,7 +5,7 @@ import dotenv from 'dotenv';
|
||||
import Logger from './logger';
|
||||
import { fileExist } from '../config/util';
|
||||
|
||||
const rootPath = process.env.QL_DIR as string;;
|
||||
const rootPath = process.env.QL_DIR as string;
|
||||
const dataPath = path.join(rootPath, 'data/');
|
||||
const configPath = path.join(dataPath, 'config/');
|
||||
const scriptPath = path.join(dataPath, 'scripts/');
|
||||
|
||||
@@ -30,7 +30,7 @@ export default async ({ server }: { server: Server }) => {
|
||||
Logger.error('Uncaught exception:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
Logger.error('Unhandled rejection:', reason);
|
||||
process.exit(1);
|
||||
|
||||
+3
-10
@@ -5,11 +5,7 @@ import { Crontab, CrontabModel, CrontabStatus } from '../data/cron';
|
||||
import { exec, execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import cron_parser from 'cron-parser';
|
||||
import {
|
||||
getFileContentByName,
|
||||
fileExist,
|
||||
killTask,
|
||||
} from '../config/util';
|
||||
import { getFileContentByName, fileExist, killTask } from '../config/util';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { Op, where, col as colFn, FindOptions, fn } from 'sequelize';
|
||||
import path from 'path';
|
||||
@@ -20,7 +16,7 @@ import { spawn } from 'cross-spawn';
|
||||
|
||||
@Service()
|
||||
export default class CronService {
|
||||
constructor(@Inject('logger') private logger: winston.Logger) { }
|
||||
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||
|
||||
private isSixCron(cron: Crontab) {
|
||||
const { schedule } = cron;
|
||||
@@ -508,10 +504,7 @@ export default class CronService {
|
||||
|
||||
private make_command(tab: Crontab) {
|
||||
let command = tab.command.trim();
|
||||
if (
|
||||
!command.startsWith(TASK_PREFIX) &&
|
||||
!command.startsWith(QL_PREFIX)
|
||||
) {
|
||||
if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) {
|
||||
command = `${TASK_PREFIX}${tab.command}`;
|
||||
}
|
||||
const crontab_job_string = `ID=${tab.id} ${command}`;
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class CronViewService {
|
||||
}
|
||||
|
||||
public async update(payload: CrontabView): Promise<CrontabView> {
|
||||
const doc = await this.getDb({ id: payload.id })
|
||||
const doc = await this.getDb({ id: payload.id });
|
||||
const tab = new CrontabView({ ...doc, ...payload });
|
||||
const newDoc = await this.updateDb(tab);
|
||||
return newDoc;
|
||||
@@ -59,7 +59,9 @@ export default class CronViewService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getDb(query: FindOptions<CrontabView>['where']): Promise<CrontabView> {
|
||||
public async getDb(
|
||||
query: FindOptions<CrontabView>['where'],
|
||||
): Promise<CrontabView> {
|
||||
const doc: any = await CrontabViewModel.findOne({ where: { ...query } });
|
||||
return doc && (doc.get({ plain: true }) as CrontabView);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class DependenceService {
|
||||
constructor(
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private sockService: SockService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
public async create(payloads: Dependence[]): Promise<Dependence[]> {
|
||||
const tabs = payloads.map((x) => {
|
||||
@@ -193,7 +193,9 @@ export default class DependenceService {
|
||||
const depVersionStr = versionDependenceCommandTypes[dependency.type];
|
||||
let depVersion = '';
|
||||
if (depName.includes(depVersionStr)) {
|
||||
const symbolRegx = new RegExp(`(.*)${depVersionStr}([0-9\\.\\-\\+a-zA-Z]*)`);
|
||||
const symbolRegx = new RegExp(
|
||||
`(.*)${depVersionStr}([0-9\\.\\-\\+a-zA-Z]*)`,
|
||||
);
|
||||
const [, _depName, _depVersion] = depName.match(symbolRegx) || [];
|
||||
if (_depVersion && _depName) {
|
||||
depName = _depName;
|
||||
@@ -202,19 +204,23 @@ export default class DependenceService {
|
||||
}
|
||||
const isNodeDependence = dependency.type === DependenceTypes.nodejs;
|
||||
const isLinuxDependence = dependency.type === DependenceTypes.linux;
|
||||
const isPythonDependence = dependency.type === DependenceTypes.python3;
|
||||
const isPythonDependence =
|
||||
dependency.type === DependenceTypes.python3;
|
||||
const depInfo = (
|
||||
await promiseExecSuccess(
|
||||
isNodeDependence
|
||||
? `${getCommandPrefix} | grep "${depName}" | head -1`
|
||||
: `${getCommandPrefix} ${depName}`,
|
||||
)
|
||||
).replace(/\s{2,}/, ' ').replace(/\s+$/, '');
|
||||
)
|
||||
.replace(/\s{2,}/, ' ')
|
||||
.replace(/\s+$/, '');
|
||||
|
||||
if (
|
||||
depInfo &&
|
||||
((isNodeDependence && depInfo.split(' ')?.[0] === depName) ||
|
||||
(isLinuxDependence && depInfo.toLocaleLowerCase().includes('installed')) ||
|
||||
(isLinuxDependence &&
|
||||
depInfo.toLocaleLowerCase().includes('installed')) ||
|
||||
isPythonDependence) &&
|
||||
(!depVersion || depInfo.includes(depVersion))
|
||||
) {
|
||||
|
||||
+10
-6
@@ -304,7 +304,8 @@ export default class NotificationService {
|
||||
}
|
||||
|
||||
private async weWorkBot() {
|
||||
const { weWorkBotKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } = this.params;
|
||||
const { weWorkBotKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } =
|
||||
this.params;
|
||||
const url = `${weWorkOrigin}/cgi-bin/webhook/send?key=${weWorkBotKey}`;
|
||||
try {
|
||||
const res: any = await got
|
||||
@@ -329,7 +330,8 @@ export default class NotificationService {
|
||||
}
|
||||
|
||||
private async weWorkApp() {
|
||||
const { weWorkAppKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } = this.params;
|
||||
const { weWorkAppKey, weWorkOrigin = 'https://qyapi.weixin.qq.com' } =
|
||||
this.params;
|
||||
const [corpid, corpsecret, touser, agentid, thumb_media_id = '1'] =
|
||||
weWorkAppKey.split(',');
|
||||
const url = `${weWorkOrigin}/cgi-bin/gettoken`;
|
||||
@@ -565,15 +567,17 @@ export default class NotificationService {
|
||||
private async pushMe() {
|
||||
const { pushMeKey } = this.params;
|
||||
try {
|
||||
const res: any = await got
|
||||
.post(`https://push.i-i.me/?push_key=${pushMeKey}`, {
|
||||
const res: any = await got.post(
|
||||
`https://push.i-i.me/?push_key=${pushMeKey}`,
|
||||
{
|
||||
...this.gotOption,
|
||||
json: {
|
||||
title: this.title,
|
||||
content: this.content
|
||||
content: this.content,
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
},
|
||||
);
|
||||
if (res.body === 'success') {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -42,7 +42,7 @@ export default class ScheduleService {
|
||||
|
||||
private maxBuffer = 200 * 1024 * 1024;
|
||||
|
||||
constructor(@Inject('logger') private logger: winston.Logger) { }
|
||||
constructor(@Inject('logger') private logger: winston.Logger) {}
|
||||
|
||||
async runTask(
|
||||
command: string,
|
||||
@@ -109,7 +109,7 @@ export default class ScheduleService {
|
||||
await callbacks.onError?.(JSON.stringify(error));
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async createCronTask(
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class SystemService {
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private scheduleService: ScheduleService,
|
||||
private sockService: SockService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
public async getSystemConfig() {
|
||||
const doc = await this.getDb({ type: AuthDataType.systemConfig });
|
||||
@@ -111,7 +111,7 @@ export default class SystemService {
|
||||
},
|
||||
);
|
||||
lastVersionContent = await parseContentVersion(result.body);
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
|
||||
if (!lastVersionContent) {
|
||||
lastVersionContent = currentVersionContent;
|
||||
@@ -256,7 +256,10 @@ export default class SystemService {
|
||||
|
||||
public async exportData(res: Response) {
|
||||
try {
|
||||
await tar.create({ gzip: true, file: config.dataTgzFile, cwd: config.rootPath }, ['data'])
|
||||
await tar.create(
|
||||
{ gzip: true, file: config.dataTgzFile, cwd: config.rootPath },
|
||||
['data'],
|
||||
);
|
||||
res.download(config.dataTgzFile);
|
||||
} catch (error: any) {
|
||||
return res.send({ code: 400, message: error.message });
|
||||
|
||||
+15
-5
@@ -10,7 +10,13 @@ import config from '../config';
|
||||
import * as fs from 'fs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { authenticator } from '@otplib/preset-default';
|
||||
import { AuthDataType, AuthInfo, AuthModel, AuthModelInfo, LoginStatus } from '../data/auth';
|
||||
import {
|
||||
AuthDataType,
|
||||
AuthInfo,
|
||||
AuthModel,
|
||||
AuthModelInfo,
|
||||
LoginStatus,
|
||||
} from '../data/auth';
|
||||
import { NotificationInfo } from '../data/notify';
|
||||
import NotificationService from './notify';
|
||||
import { Request } from 'express';
|
||||
@@ -27,7 +33,7 @@ export default class UserService {
|
||||
@Inject('logger') private logger: winston.Logger,
|
||||
private scheduleService: ScheduleService,
|
||||
private sockService: SockService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
public async login(
|
||||
payloads: {
|
||||
@@ -119,7 +125,8 @@ export default class UserService {
|
||||
});
|
||||
await this.notificationService.notify(
|
||||
'登录通知',
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${req.platform
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${
|
||||
req.platform
|
||||
}端 登录成功,ip地址 ${ip}`,
|
||||
);
|
||||
await this.getLoginLog();
|
||||
@@ -147,7 +154,8 @@ export default class UserService {
|
||||
});
|
||||
await this.notificationService.notify(
|
||||
'登录通知',
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${req.platform
|
||||
`你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${
|
||||
req.platform
|
||||
}端 登录失败,ip地址 ${ip}`,
|
||||
);
|
||||
await this.getLoginLog();
|
||||
@@ -190,7 +198,9 @@ export default class UserService {
|
||||
where: { type: AuthDataType.loginLog },
|
||||
});
|
||||
if (docs && docs.length > 0) {
|
||||
const result = docs.sort((a, b) => b.info!.timestamp! - a.info!.timestamp!);
|
||||
const result = docs.sort(
|
||||
(a, b) => b.info!.timestamp! - a.info!.timestamp!,
|
||||
);
|
||||
if (result.length > 100) {
|
||||
await AuthModel.destroy({
|
||||
where: { id: result[result.length - 1].id },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pLimit from "p-limit";
|
||||
import pLimit from 'p-limit';
|
||||
import os from 'os';
|
||||
import { AuthDataType, AuthModel } from "../data/auth";
|
||||
import { AuthDataType, AuthModel } from '../data/auth';
|
||||
|
||||
class TaskLimit {
|
||||
private oneLimit = pLimit(1);
|
||||
@@ -17,7 +17,9 @@ class TaskLimit {
|
||||
return;
|
||||
}
|
||||
await AuthModel.sync();
|
||||
const doc = await AuthModel.findOne({ where: { type: AuthDataType.systemConfig } });
|
||||
const doc = await AuthModel.findOne({
|
||||
where: { type: AuthDataType.systemConfig },
|
||||
});
|
||||
if (doc?.info?.cronConcurrency) {
|
||||
this.cpuLimit = pLimit(doc?.info?.cronConcurrency);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { spawn } from 'cross-spawn';
|
||||
import taskLimit from "./pLimit";
|
||||
import taskLimit from './pLimit';
|
||||
import Logger from '../loaders/logger';
|
||||
|
||||
export function runCron(cmd: string): Promise<number> {
|
||||
@@ -27,11 +27,9 @@ export function runCron(cmd: string): Promise<number> {
|
||||
});
|
||||
|
||||
cp.on('close', async (code) => {
|
||||
Logger.info(
|
||||
`[任务退出] ${cmd} 进程id: ${cp.pid} 退出,退出码 ${code}`,
|
||||
);
|
||||
Logger.info(`[任务退出] ${cmd} 进程id: ${cp.pid} 退出,退出码 ${code}`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user