Compare commits

...
12 Commits
Author SHA1 Message Date
whyour acb43f3ad8 更新版本 v2.13.2 2022-06-01 00:28:35 +08:00
whyour b160f914ad 修改订阅执行逻辑 2022-06-01 00:25:32 +08:00
whyour 6116aa6d12 修复私钥配置文件匹配 2022-06-01 00:03:58 +08:00
whyour d43cee53c9 修改interval_schedule验证 2022-05-30 10:26:30 +08:00
whyour defea8ff9b 修复订阅添加定时规则 2022-05-30 10:21:15 +08:00
whyour b290b60d46 修复新建订阅定时 2022-05-30 09:58:51 +08:00
whyour 706b5e8108 修复订阅任务创建逻辑 2022-05-29 22:51:36 +08:00
whyour b55c7d5747 修复依赖安装顺序,优先linux依赖安装 2022-05-29 15:01:48 +08:00
whyour 6b8fd94f0c 系统启动安装依赖优先安装linux依赖 2022-05-29 11:35:31 +08:00
whyour e18710a30a 更新dockerfile 2022-05-29 11:13:32 +08:00
whyour c0f0da4e96 修复dockerfile 2022-05-29 10:18:08 +08:00
whyour 49ef5cd61f 修复pnpm环境变量 2022-05-29 10:02:03 +08:00
11 changed files with 78 additions and 36 deletions
+7 -1
View File
@@ -29,7 +29,13 @@ export default (app: Router) => {
body: Joi.object({ body: Joi.object({
type: Joi.string().required(), type: Joi.string().required(),
schedule: Joi.string().optional().allow('').allow(null), schedule: Joi.string().optional().allow('').allow(null),
interval_schedule: Joi.object().optional().allow('').allow(null), interval_schedule: Joi.object({
type: Joi.string().required(),
value: Joi.number().min(1).required(),
})
.optional()
.allow('')
.allow(null),
name: Joi.string().optional().allow('').allow(null), name: Joi.string().optional().allow('').allow(null),
url: Joi.string().required(), url: Joi.string().required(),
whitelist: Joi.string().optional().allow('').allow(null), whitelist: Joi.string().optional().allow('').allow(null),
+7 -4
View File
@@ -21,15 +21,18 @@ export default async () => {
); );
// 初始化时安装所有处于安装中,安装成功,安装失败的依赖 // 初始化时安装所有处于安装中,安装成功,安装失败的依赖
DependenceModel.findAll({ where: {}, raw: true }).then(async (docs) => { DependenceModel.findAll({
where: {},
order: [['type', 'DESC']],
raw: true,
}).then(async (docs) => {
const groups = _.groupBy(docs, 'type'); const groups = _.groupBy(docs, 'type');
for (const key in groups) { const keys = Object.keys(groups).sort((a, b) => parseInt(b) - parseInt(a));
if (Object.prototype.hasOwnProperty.call(groups, key)) { for (const key of keys) {
const group = groups[key]; const group = groups[key];
const depIds = group.map((x) => x.id); const depIds = group.map((x) => x.id);
await dependenceService.reInstall(depIds as number[]); await dependenceService.reInstall(depIds as number[]);
} }
}
}); });
// 初始化时执行一次所有的ql repo 任务 // 初始化时执行一次所有的ql repo 任务
+6 -1
View File
@@ -26,6 +26,11 @@ export default async () => {
// 运行所有订阅 // 运行所有订阅
const subs = await subscriptionService.list(); const subs = await subscriptionService.list();
for (const sub of subs) { for (const sub of subs) {
await subscriptionService.handleTask(sub, true, true, true); await subscriptionService.handleTask(
sub,
!sub.is_disabled,
true,
!sub.is_disabled,
);
} }
}; };
+3
View File
@@ -18,6 +18,7 @@ interface ScheduleTaskType {
} }
export interface TaskCallbacks { export interface TaskCallbacks {
onBefore?: (startTime: dayjs.Dayjs) => Promise<void>;
onStart?: ( onStart?: (
cp: ChildProcessWithoutNullStreams, cp: ChildProcessWithoutNullStreams,
startTime: dayjs.Dayjs, startTime: dayjs.Dayjs,
@@ -45,6 +46,8 @@ export default class ScheduleService {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const startTime = dayjs(); const startTime = dayjs();
await callbacks.onBefore?.(startTime);
const cp = spawn(command, { shell: '/bin/bash' }); const cp = spawn(command, { shell: '/bin/bash' });
// TODO: // TODO:
+13 -5
View File
@@ -23,6 +23,13 @@ export default class SshKeyService {
} }
} }
private getConfigRegx(alias: string) {
return new RegExp(
`Host ${alias}\n.*[^StrictHostKeyChecking]*.*[\n]*.*StrictHostKeyChecking no`,
'g',
);
}
private removePrivateKeyFile(alias: string): void { private removePrivateKeyFile(alias: string): void {
try { try {
fs.unlinkSync(`${this.sshPath}/${alias}`); fs.unlinkSync(`${this.sshPath}/${alias}`);
@@ -47,24 +54,25 @@ export default class SshKeyService {
} }
} }
private removeSshConfig(config: string) { private removeSshConfig(alias: string) {
try { try {
const configRegx = this.getConfigRegx(alias);
const data = fs const data = fs
.readFileSync(this.sshConfigFilePath, { encoding: 'utf8' }) .readFileSync(this.sshConfigFilePath, { encoding: 'utf8' })
.replace(config, '') .replace(configRegx, '')
.replace(/\n\n+/, '\n\n'); .replace(/\n[\n]+/g, '\n');
fs.writeFileSync(this.sshConfigFilePath, data, { fs.writeFileSync(this.sshConfigFilePath, data, {
encoding: 'utf8', encoding: 'utf8',
}); });
} catch (error) { } catch (error) {
this.logger.error(`删除ssh配置文件${config}失败`, error); this.logger.error(`删除ssh配置文件${alias}失败`, error);
} }
} }
public addSSHKey(key: string, alias: string, host: string): void { public addSSHKey(key: string, alias: string, host: string): void {
this.generatePrivateKeyFile(alias, key); this.generatePrivateKeyFile(alias, key);
const config = this.generateSingleSshConfig(alias, host); const config = this.generateSingleSshConfig(alias, host);
this.removeSshConfig(config); this.removeSshConfig(alias);
this.generateSshConfig([config]); this.generateSshConfig([config]);
} }
+12 -6
View File
@@ -192,14 +192,13 @@ export default class SubscriptionService {
private taskCallbacks(doc: Subscription): TaskCallbacks { private taskCallbacks(doc: Subscription): TaskCallbacks {
return { return {
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => { onBefore: async (startTime) => {
const logTime = startTime.format('YYYY-MM-DD-HH-mm-ss'); const logTime = startTime.format('YYYY-MM-DD-HH-mm-ss');
const logPath = `${doc.alias}/${logTime}.log`; const logPath = `${doc.alias}/${logTime}.log`;
await SubscriptionModel.update( await SubscriptionModel.update(
{ {
status: SubscriptionStatus.running, status: SubscriptionStatus.running,
log_path: logPath, log_path: logPath,
pid: cp.pid,
}, },
{ where: { id: doc.id } }, { where: { id: doc.id } },
); );
@@ -220,9 +219,17 @@ export default class SubscriptionService {
(error.stderr && error.stderr.toString()) || JSON.stringify(error); (error.stderr && error.stderr.toString()) || JSON.stringify(error);
} }
if (beforeStr) { if (beforeStr) {
fs.appendFileSync(absolutePath, `${beforeStr}\n\n`); fs.appendFileSync(absolutePath, `${beforeStr}\n`);
} }
}, },
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
await SubscriptionModel.update(
{
pid: cp.pid,
},
{ where: { id: doc.id } },
);
},
onEnd: async (cp, endTime, diff) => { onEnd: async (cp, endTime, diff) => {
const sub = await this.getDb({ id: doc.id }); const sub = await this.getDb({ id: doc.id });
const absolutePath = await this.handleLogPath(sub.log_path as string); const absolutePath = await this.handleLogPath(sub.log_path as string);
@@ -231,7 +238,7 @@ export default class SubscriptionService {
let afterStr = ''; let afterStr = '';
try { try {
if (sub.sub_after) { if (sub.sub_after) {
fs.appendFileSync(absolutePath, `\n\n## 执行after命令...\n`); fs.appendFileSync(absolutePath, `\n\n## 执行after命令...\n\n`);
afterStr = await this.promiseExec(sub.sub_after); afterStr = await this.promiseExec(sub.sub_after);
} }
} catch (error: any) { } catch (error: any) {
@@ -286,7 +293,7 @@ export default class SubscriptionService {
public async update(payload: Subscription): Promise<Subscription> { public async update(payload: Subscription): Promise<Subscription> {
const newDoc = await this.updateDb(payload); const newDoc = await this.updateDb(payload);
await this.handleTask(newDoc); await this.handleTask(newDoc, !newDoc.is_disabled);
return newDoc; return newDoc;
} }
@@ -360,7 +367,6 @@ export default class SubscriptionService {
this.logger.silly(error); this.logger.silly(error);
} }
} }
await this.handleTask(doc, false);
const command = this.formatCommand(doc); const command = this.formatCommand(doc);
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);
+2 -3
View File
@@ -5,7 +5,8 @@ LABEL maintainer="${QL_MAINTAINER}"
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
ARG QL_BRANCH=master ARG QL_BRANCH=master
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/share/pnpm:/root/.local/share/pnpm/global/5/node_modules \ ENV PNPM_HOME=/root/.local/share/pnpm \
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/share/pnpm:/root/.local/share/pnpm/global/5/node_modules:$PNPM_HOME \
LANG=zh_CN.UTF-8 \ LANG=zh_CN.UTF-8 \
SHELL=/bin/bash \ SHELL=/bin/bash \
PS1="\u@\h:\w \$ " \ PS1="\u@\h:\w \$ " \
@@ -38,8 +39,6 @@ RUN set -x \
&& git config --global user.email "qinglong@@users.noreply.github.com" \ && git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \ && git config --global user.name "qinglong" \
&& npm install -g pnpm \ && npm install -g pnpm \
&& pnpm setup \
&& source ~/.bashrc \
&& pnpm add -g pm2 ts-node typescript tslib \ && pnpm add -g pm2 ts-node typescript tslib \
&& git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ && git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
&& cd ${QL_DIR} \ && cd ${QL_DIR} \
+3 -3
View File
@@ -40,8 +40,8 @@
}, },
"dependencies": { "dependencies": {
"@otplib/preset-default": "^12.0.1", "@otplib/preset-default": "^12.0.1",
"@sentry/node": "^6.18.1", "@sentry/node": "^7.0.0",
"@sentry/tracing": "^6.18.1", "@sentry/tracing": "^7.0.0",
"body-parser": "^1.19.2", "body-parser": "^1.19.2",
"celebrate": "^15.0.1", "celebrate": "^15.0.1",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
@@ -77,7 +77,7 @@
"@ant-design/icons": "^4.7.0", "@ant-design/icons": "^4.7.0",
"@ant-design/pro-layout": "^6.33.1", "@ant-design/pro-layout": "^6.33.1",
"@monaco-editor/react": "^4.3.1", "@monaco-editor/react": "^4.3.1",
"@sentry/react": "^6.18.1", "@sentry/react": "^7.0.0",
"@types/body-parser": "^1.19.2", "@types/body-parser": "^1.19.2",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
+6
View File
@@ -107,12 +107,18 @@ const SubscriptionModal = ({
const [intervalNumber, setIntervalNumber] = useState<number>(); const [intervalNumber, setIntervalNumber] = useState<number>();
const intervalTypeChange = (type: string) => { const intervalTypeChange = (type: string) => {
setIntervalType(type); setIntervalType(type);
if (intervalNumber && intervalNumber > 0) {
onChange?.({ type, value: intervalNumber }); onChange?.({ type, value: intervalNumber });
}
}; };
const numberChange = (value: number) => { const numberChange = (value: number) => {
setIntervalNumber(value); setIntervalNumber(value);
if (!value) {
onChange?.(null);
} else {
onChange?.({ type: intervalType, value }); onChange?.({ type: intervalType, value });
}
}; };
useEffect(() => { useEffect(() => {
+8 -2
View File
@@ -7,9 +7,15 @@ export function init() {
// sentry监控 init // sentry监控 init
Sentry.init({ Sentry.init({
dsn: 'https://3406424fb1dc4813a62d39e844a9d0ac@o1098464.ingest.sentry.io/6122818', dsn: 'https://3406424fb1dc4813a62d39e844a9d0ac@o1098464.ingest.sentry.io/6122818',
integrations: [new Integrations.BrowserTracing()], integrations: [
new Integrations.BrowserTracing({
shouldCreateSpanForRequest(url) {
return !url.includes('/api/ws') && !url.includes('/api/static');
},
}),
],
release: version, release: version,
tracesSampleRate: 1.0, tracesSampleRate: 0.1,
beforeBreadcrumb(breadcrumb, hint?) { beforeBreadcrumb(breadcrumb, hint?) {
if (breadcrumb.data && breadcrumb.data.url) { if (breadcrumb.data && breadcrumb.data.url) {
const url = breadcrumb.data.url.replace(/token=.*/, ''); const url = breadcrumb.data.url.replace(/token=.*/, '');
+6 -6
View File
@@ -1,8 +1,8 @@
export const version = '2.13.1'; export const version = '2.13.2';
export const changeLogLink = 'https://t.me/jiao_long/303'; export const changeLogLink = 'https://t.me/jiao_long/303';
export const changeLog = `2.13.1 版本说明 export const changeLog = `2.13.2 版本说明
1. 修复新建订阅repo命令快捷导入 1. 修复nodejs依赖安装失败pnpm setup
2. 修复执行订阅任务含有before/after命令时,造成面板无法访问 2. 修复订阅禁用失效
3. 修复任务详情日志列表访问 3. 修复新建订阅定时验证
4. 修复安装依赖造成CPU满负荷,服务器崩溃 4. 修复ssh配置文件更新逻辑
`; `;