Compare commits

..

24 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
whyour 99d7eaeff0 更新版本 v2.13.1 2022-05-29 01:50:46 +08:00
whyour c8f5eb7c2e 修复pnpm版本兼容 2022-05-29 01:50:05 +08:00
whyour 109e16e8fb 修复前端列表查询 2022-05-28 21:49:34 +08:00
whyour 555f6e04e8 修复依赖日志查看 2022-05-28 21:10:24 +08:00
whyour bcde0a54bf 修复依赖安装日志 2022-05-28 20:51:25 +08:00
whyour eee7f25bc3 修复订阅日志查看 2022-05-28 20:11:38 +08:00
whyour b10157b09a 修复系统启动执行订阅任务 2022-05-28 19:56:12 +08:00
whyour a09a21fa5d 依赖改为串行安装 2022-05-28 19:41:37 +08:00
whyour ec2918b427 修改订阅日志排版 2022-05-28 19:04:46 +08:00
whyour 57bfc19ecf 修复execSync卡死主进程 2022-05-28 18:55:52 +08:00
whyour aa110d4187 修复任务详情日志访问,修复新建订阅粘贴repo命令 2022-05-28 17:36:41 +08:00
whyour 9e8febfc11 修复七牛sdk参数 2022-05-28 00:11:43 +08:00
21 changed files with 309 additions and 179 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ export default (app: Router) => {
try {
const filePath = join(
config.logPath,
req.query.path as string,
(req.query.path || '') as string,
req.params.file,
);
const content = getFileContentByName(filePath);
+7 -1
View File
@@ -29,7 +29,13 @@ export default (app: Router) => {
body: Joi.object({
type: Joi.string().required(),
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),
url: Joi.string().required(),
whitelist: Joi.string().optional().allow('').allow(null),
+10 -11
View File
@@ -21,18 +21,17 @@ 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');
for (const key in groups) {
if (Object.prototype.hasOwnProperty.call(groups, key)) {
const group = groups[key];
const depIds = group.map((x) => x.id);
for (const dep of depIds) {
if (dep) {
await dependenceService.reInstall([dep]);
}
}
}
const keys = Object.keys(groups).sort((a, b) => parseInt(b) - parseInt(a));
for (const key of keys) {
const group = groups[key];
const depIds = group.map((x) => x.id);
await dependenceService.reInstall(depIds as number[]);
}
});
+6 -1
View File
@@ -26,6 +26,11 @@ export default async () => {
// 运行所有订阅
const subs = await subscriptionService.list();
for (const sub of subs) {
await subscriptionService.handleTask(sub);
await subscriptionService.handleTask(
sub,
!sub.is_disabled,
true,
!sub.is_disabled,
);
}
};
+19 -4
View File
@@ -13,6 +13,7 @@ import _ from 'lodash';
import { spawn } from 'child_process';
import SockService from './sock';
import { Op } from 'sequelize';
import { concurrentRun } from '../config/util';
@Service()
export default class DependenceService {
@@ -27,7 +28,7 @@ export default class DependenceService {
return tab;
});
const docs = await this.insert(tabs);
this.installOrUninstallDependencies(docs);
this.installDependenceOneByOne(docs);
return docs;
}
@@ -47,7 +48,7 @@ export default class DependenceService {
status: DependenceStatus.installing,
});
const newDoc = await this.updateDb(tab);
this.installOrUninstallDependencies([newDoc]);
this.installDependenceOneByOne([newDoc]);
return newDoc;
}
@@ -62,7 +63,7 @@ export default class DependenceService {
{ where: { id: ids } },
);
const docs = await DependenceModel.findAll({ where: { id: ids } });
this.installOrUninstallDependencies(docs, false, force);
this.installDependenceOneByOne(docs, false, force);
return docs;
}
@@ -98,6 +99,20 @@ export default class DependenceService {
}
}
private installDependenceOneByOne(
docs: Dependence[],
isInstall: boolean = true,
force: boolean = false,
) {
concurrentRun(
docs.map(
(dep) => async () =>
await this.installOrUninstallDependencies([dep], isInstall, force),
),
1,
);
}
public async reInstall(ids: number[]): Promise<Dependence[]> {
await DependenceModel.update(
{ status: DependenceStatus.installing, log: [] },
@@ -105,7 +120,7 @@ export default class DependenceService {
);
const docs = await DependenceModel.findAll({ where: { id: ids } });
this.installOrUninstallDependencies(docs);
this.installDependenceOneByOne(docs);
return docs;
}
+17 -1
View File
@@ -18,6 +18,7 @@ interface ScheduleTaskType {
}
export interface TaskCallbacks {
onBefore?: (startTime: dayjs.Dayjs) => Promise<void>;
onStart?: (
cp: ChildProcessWithoutNullStreams,
startTime: dayjs.Dayjs,
@@ -45,6 +46,8 @@ export default class ScheduleService {
return new Promise(async (resolve, reject) => {
try {
const startTime = dayjs();
await callbacks.onBefore?.(startTime);
const cp = spawn(command, { shell: '/bin/bash' });
// TODO:
@@ -106,6 +109,7 @@ export default class ScheduleService {
async createCronTask(
{ id = 0, command, name, schedule = '' }: ScheduleTaskType,
callbacks?: TaskCallbacks,
runImmediately = false,
) {
const _id = this.formatId(id);
this.logger.info(
@@ -122,6 +126,10 @@ export default class ScheduleService {
await this.runTask(command, callbacks);
}),
);
if (runImmediately) {
await this.runTask(command, callbacks);
}
}
async cancelCronTask({ id = 0, name }: ScheduleTaskType) {
@@ -160,9 +168,17 @@ export default class ScheduleService {
},
);
const job = new LongIntervalJob({ ...schedule, runImmediately }, task, _id);
const job = new LongIntervalJob(
{ ...schedule, runImmediately: false },
task,
_id,
);
this.intervalSchedule.addIntervalJob(job);
if (runImmediately) {
await this.runTask(command, callbacks);
}
}
async cancelIntervalTask({ id = 0, name }: ScheduleTaskType) {
+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 {
try {
fs.unlinkSync(`${this.sshPath}/${alias}`);
@@ -47,24 +54,25 @@ export default class SshKeyService {
}
}
private removeSshConfig(config: string) {
private removeSshConfig(alias: string) {
try {
const configRegx = this.getConfigRegx(alias);
const data = fs
.readFileSync(this.sshConfigFilePath, { encoding: 'utf8' })
.replace(config, '')
.replace(/\n\n+/, '\n\n');
.replace(configRegx, '')
.replace(/\n[\n]+/g, '\n');
fs.writeFileSync(this.sshConfigFilePath, data, {
encoding: 'utf8',
});
} catch (error) {
this.logger.error(`删除ssh配置文件${config}失败`, error);
this.logger.error(`删除ssh配置文件${alias}失败`, error);
}
}
public addSSHKey(key: string, alias: string, host: string): void {
this.generatePrivateKeyFile(alias, key);
const config = this.generateSingleSshConfig(alias, host);
this.removeSshConfig(config);
this.removeSshConfig(alias);
this.generateSshConfig([config]);
}
+77 -51
View File
@@ -124,7 +124,12 @@ export default class SubscriptionService {
return { url, host };
}
public handleTask(doc: Subscription, needCreate = true, needAddKey = true) {
public async handleTask(
doc: Subscription,
needCreate = true,
needAddKey = true,
runImmediately = false,
) {
const { url, host } = this.formatUrl(doc);
if (doc.type === 'private-repo' && doc.pull_type === 'ssh-key') {
if (needAddKey) {
@@ -143,23 +148,36 @@ export default class SubscriptionService {
if (doc.schedule_type === 'crontab') {
this.scheduleService.cancelCronTask(doc as any);
needCreate &&
this.scheduleService.createCronTask(
(await this.scheduleService.createCronTask(
doc as any,
this.taskCallbacks(doc),
);
runImmediately,
));
} else {
this.scheduleService.cancelIntervalTask(doc as any);
const { type, value } = doc.interval_schedule as any;
needCreate &&
this.scheduleService.createIntervalTask(
(await this.scheduleService.createIntervalTask(
doc as any,
{ [type]: value } as SimpleIntervalSchedule,
true,
runImmediately,
this.taskCallbacks(doc),
);
));
}
}
private async 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));
},
);
});
}
private async handleLogPath(
logPath: string,
data: string = '',
@@ -174,32 +192,39 @@ export default class SubscriptionService {
private taskCallbacks(doc: Subscription): TaskCallbacks {
return {
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
// 执行sub_before
let beforeStr = '';
try {
if (doc.sub_before) {
beforeStr = execSync(doc.sub_before).toString();
}
} catch (error) {
beforeStr = JSON.stringify(error);
}
if (beforeStr) {
beforeStr += '\n';
}
onBefore: async (startTime) => {
const logTime = startTime.format('YYYY-MM-DD-HH-mm-ss');
const logPath = `${doc.alias}/${logTime}.log`;
await this.handleLogPath(
logPath as string,
`${beforeStr}## 开始执行... ${startTime.format(
'YYYY-MM-DD HH:mm:ss',
)}\n`,
);
await SubscriptionModel.update(
{
status: SubscriptionStatus.running,
log_path: logPath,
},
{ where: { id: doc.id } },
);
const absolutePath = await this.handleLogPath(
logPath as string,
`## 开始执行... ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n`,
);
// 执行sub_before
let beforeStr = '';
try {
if (doc.sub_before) {
fs.appendFileSync(absolutePath, `\n## 执行before命令...\n\n`);
beforeStr = await this.promiseExec(doc.sub_before);
}
} catch (error: any) {
beforeStr =
(error.stderr && error.stderr.toString()) || JSON.stringify(error);
}
if (beforeStr) {
fs.appendFileSync(absolutePath, `${beforeStr}\n`);
}
},
onStart: async (cp: ChildProcessWithoutNullStreams, startTime) => {
await SubscriptionModel.update(
{
pid: cp.pid,
},
{ where: { id: doc.id } },
@@ -207,11 +232,23 @@ export default class SubscriptionService {
},
onEnd: async (cp, endTime, diff) => {
const sub = await this.getDb({ id: doc.id });
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: undefined },
{ where: { id: sub.id } },
);
const absolutePath = await this.handleLogPath(sub.log_path as string);
// 执行 sub_after
let afterStr = '';
try {
if (sub.sub_after) {
fs.appendFileSync(absolutePath, `\n\n## 执行after命令...\n\n`);
afterStr = await this.promiseExec(sub.sub_after);
}
} catch (error: any) {
afterStr =
(error.stderr && error.stderr.toString()) || JSON.stringify(error);
}
if (afterStr) {
fs.appendFileSync(absolutePath, `${afterStr}\n`);
}
fs.appendFileSync(
absolutePath,
`\n## 执行结束... ${endTime.format(
@@ -219,20 +256,10 @@ export default class SubscriptionService {
)} 耗时 ${diff}`,
);
// 执行 sub_after
let afterStr = '';
try {
if (sub.sub_after) {
afterStr = execSync(sub.sub_after).toString();
}
} catch (error) {
afterStr = JSON.stringify(error);
}
if (afterStr) {
afterStr = `\n\n${afterStr}`;
const absolutePath = await this.handleLogPath(sub.log_path as string);
fs.appendFileSync(absolutePath, afterStr);
}
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: undefined },
{ where: { id: sub.id } },
);
this.sockService.sendMessage({
type: 'runSubscriptionEnd',
@@ -256,7 +283,7 @@ export default class SubscriptionService {
public async create(payload: Subscription): Promise<Subscription> {
const tab = new Subscription(payload);
const doc = await this.insert(tab);
this.handleTask(doc);
await this.handleTask(doc);
return doc;
}
@@ -266,7 +293,7 @@ export default class SubscriptionService {
public async update(payload: Subscription): Promise<Subscription> {
const newDoc = await this.updateDb(payload);
this.handleTask(newDoc);
await this.handleTask(newDoc, !newDoc.is_disabled);
return newDoc;
}
@@ -309,7 +336,7 @@ export default class SubscriptionService {
public async remove(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc, false, false);
await this.handleTask(doc, false, false);
}
await SubscriptionModel.destroy({ where: { id: ids } });
}
@@ -340,7 +367,6 @@ export default class SubscriptionService {
this.logger.silly(error);
}
}
this.handleTask(doc, false);
const command = this.formatCommand(doc);
const err = await this.killTask(command);
const absolutePath = await this.handleLogPath(doc.log_path as string);
@@ -411,7 +437,7 @@ export default class SubscriptionService {
public async disabled(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc, false);
await this.handleTask(doc, false);
}
await SubscriptionModel.update({ is_disabled: 1 }, { where: { id: ids } });
}
@@ -419,14 +445,14 @@ export default class SubscriptionService {
public async enabled(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
for (const doc of docs) {
this.handleTask(doc);
await this.handleTask(doc);
}
await SubscriptionModel.update({ is_disabled: 0 }, { where: { id: ids } });
}
public async log(id: number) {
const doc = await this.getDb({ id });
if (!doc) {
if (!doc || !doc.log_path) {
return '';
}
+2 -3
View File
@@ -5,7 +5,8 @@ LABEL maintainer="${QL_MAINTAINER}"
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
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 \
SHELL=/bin/bash \
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.name "qinglong" \
&& npm install -g pnpm \
&& pnpm setup \
&& source ~/.bashrc \
&& pnpm add -g pm2 ts-node typescript tslib \
&& git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
&& cd ${QL_DIR} \
+3 -3
View File
@@ -40,8 +40,8 @@
},
"dependencies": {
"@otplib/preset-default": "^12.0.1",
"@sentry/node": "^6.18.1",
"@sentry/tracing": "^6.18.1",
"@sentry/node": "^7.0.0",
"@sentry/tracing": "^7.0.0",
"body-parser": "^1.19.2",
"celebrate": "^15.0.1",
"chokidar": "^3.5.3",
@@ -77,7 +77,7 @@
"@ant-design/icons": "^4.7.0",
"@ant-design/pro-layout": "^6.33.1",
"@monaco-editor/react": "^4.3.1",
"@sentry/react": "^6.18.1",
"@sentry/react": "^7.0.0",
"@types/body-parser": "^1.19.2",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
+1 -1
View File
@@ -17,7 +17,7 @@ const config = new qiniu.conf.Config({ zone: qiniu.zone.Zone_z1 });
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra(
'',
'',
{},
'text/plain; charset=utf-8',
);
// 文件上传
+1
View File
@@ -305,6 +305,7 @@ patch_version() {
fi
# 兼容pnpm@7
npm i -g pnpm
pnpm setup
source ~/.bashrc
+6 -2
View File
@@ -115,9 +115,13 @@ const CronDetailModal = ({
const onClickItem = (item: LogItem) => {
localStorage.setItem('logCron', currentCron.id);
setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`);
setLogUrl(
`${config.apiPrefix}logs/${item.filename}?path=${item.directory || ''}`,
);
request
.get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`)
.get(
`${config.apiPrefix}logs/${item.filename}?path=${item.directory || ''}`,
)
.then((data) => {
setLog(data.data);
setIsLogModalVisible(true);
+40 -28
View File
@@ -438,8 +438,10 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
message.success('删除成功');
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1);
setValue(result);
if (i !== -1) {
result.splice(i, 1);
setValue(result);
}
} else {
message.error(data);
}
@@ -470,11 +472,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
status: CrontabStatus.running,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
status: CrontabStatus.running,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -505,12 +509,14 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
pid: null,
status: CrontabStatus.idle,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
pid: null,
status: CrontabStatus.idle,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -550,11 +556,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const newStatus = record.isDisabled === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
isDisabled: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
isDisabled: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -594,11 +602,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const newStatus = record.isPinned === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
isPinned: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
isPinned: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -709,11 +719,13 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
.parseExpression(data.data.schedule)
.next()
.toDate();
result.splice(index, 1, {
...cron,
...data.data,
});
setValue(result);
if (index !== -1) {
result.splice(index, 1, {
...cron,
...data.data,
});
setValue(result);
}
})
.finally(() => setLoading(false));
};
+25 -15
View File
@@ -271,9 +271,11 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
result.push(...dependence);
} else {
const index = value.findIndex((x) => x.id === dependence.id);
result.splice(index, 1, {
...dependence,
});
if (index !== -1) {
result.splice(index, 1, {
...dependence,
});
}
}
setValue(result);
};
@@ -324,11 +326,13 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
.then((data: any) => {
const index = value.findIndex((x) => x.id === dependence.id);
const result = [...value];
result.splice(index, 1, {
...dependence,
...data.data,
});
setValue(result);
if (index !== -1) {
result.splice(index, 1, {
...dependence,
...data.data,
});
setValue(result);
}
})
.finally(() => setLoading(false));
};
@@ -371,10 +375,12 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
const result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
result.splice(index, 1, {
...result[index],
status,
});
if (index !== -1) {
result.splice(index, 1, {
...value[index],
status,
});
}
}
setValue(result);
@@ -383,7 +389,9 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
const _result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
_result.splice(index, 1);
if (index !== -1) {
_result.splice(index, 1);
}
}
setValue(_result);
}, 5000);
@@ -483,8 +491,10 @@ const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
if (needRemove) {
const index = value.findIndex((x) => x.id === logDependence.id);
const result = [...value];
result.splice(index, 1);
setValue(result);
if (index !== -1) {
result.splice(index, 1);
setValue(result);
}
} else if ([...value].map((x) => x.id).includes(logDependence.id)) {
getDependenceDetail(logDependence);
}
+8 -6
View File
@@ -90,17 +90,19 @@ const DependenceLogModal = ({
}, [dependence]);
useEffect(() => {
if (!socketMessage) return;
if (!socketMessage || !dependence) return;
const { type, message, references } = socketMessage;
if (
type === 'installDependence' &&
message.includes('结束时间') &&
references.length > 0
references.length > 0 &&
references.includes(dependence.id)
) {
setExecuting(false);
setIsRemoveFailed(message.includes('删除失败'));
if (message.includes('结束时间')) {
setExecuting(false);
setIsRemoveFailed(message.includes('删除失败'));
}
setValue(`${value}${message}`);
}
setValue(`${value}${message}`);
}, [socketMessage]);
useEffect(() => {
+15 -9
View File
@@ -278,13 +278,17 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const index = parentNode.children.findIndex(
(y) => y.key === currentNode.key,
);
parentNode.children.splice(index, 1);
newData.splice(parentNodeIndex, 1, { ...parentNode });
if (index !== -1 && parentNodeIndex !== -1) {
parentNode.children.splice(index, 1);
newData.splice(parentNodeIndex, 1, { ...parentNode });
}
} else {
const index = newData.findIndex(
(x) => x.key === currentNode.key,
);
newData.splice(index, 1);
if (index !== -1) {
newData.splice(index, 1);
}
}
setData(newData);
} else {
@@ -314,13 +318,15 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const _file = { title: filename, key, value: filename, parent: path };
if (path) {
const parentNodeIndex = newData.findIndex((x) => x.key === path);
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
if (parentNodeIndex !== -1) {
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
} else {
newData.unshift(_file);
}
+32 -22
View File
@@ -263,11 +263,13 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
status: SubscriptionStatus.running,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
status: SubscriptionStatus.running,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -298,12 +300,14 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
if (data.code === 200) {
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
pid: null,
status: SubscriptionStatus.idle,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
pid: null,
status: SubscriptionStatus.idle,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -356,8 +360,10 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
message.success('删除成功');
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1);
setValue(result);
if (i !== -1) {
result.splice(i, 1);
setValue(result);
}
} else {
message.error(data);
}
@@ -397,11 +403,13 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
const newStatus = record.is_disabled === 1 ? 0 : 1;
const result = [...value];
const i = result.findIndex((x) => x.id === record.id);
result.splice(i, 1, {
...record,
is_disabled: newStatus,
});
setValue(result);
if (i !== -1) {
result.splice(i, 1, {
...record,
is_disabled: newStatus,
});
setValue(result);
}
} else {
message.error(data);
}
@@ -509,10 +517,12 @@ const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
const result = [...value];
for (let i = 0; i < references.length; i++) {
const index = value.findIndex((x) => x.id === references[i]);
result.splice(index, 1, {
...result[index],
status: SubscriptionStatus.idle,
});
if (index !== -1) {
result.splice(index, 1, {
...value[index],
status: SubscriptionStatus.idle,
});
}
}
setValue(result);
}
+12 -3
View File
@@ -107,12 +107,18 @@ const SubscriptionModal = ({
const [intervalNumber, setIntervalNumber] = useState<number>();
const intervalTypeChange = (type: string) => {
setIntervalType(type);
onChange?.({ type, value: intervalNumber });
if (intervalNumber && intervalNumber > 0) {
onChange?.({ type, value: intervalNumber });
}
};
const numberChange = (value: number) => {
setIntervalNumber(value);
onChange?.({ type: intervalType, value });
if (!value) {
onChange?.(null);
} else {
onChange?.({ type: intervalType, value });
}
};
useEffect(() => {
@@ -195,7 +201,9 @@ const SubscriptionModal = ({
dependences,
branch,
extensions,
] = text.split(' ').map((x) => x.trim());
] = text
.split(' ')
.map((x) => x.trim().replace(/\"/g, '').replace(/\'/, ''));
form.setFieldsValue({
type:
type === 'raw'
@@ -209,6 +217,7 @@ const SubscriptionModal = ({
dependences,
branch,
extensions,
alias: formatAlias(url, branch),
});
}
}, []);
+8 -2
View File
@@ -7,9 +7,15 @@ export function init() {
// sentry监控 init
Sentry.init({
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,
tracesSampleRate: 1.0,
tracesSampleRate: 0.1,
beforeBreadcrumb(breadcrumb, hint?) {
if (breadcrumb.data && breadcrumb.data.url) {
const url = breadcrumb.data.url.replace(/token=.*/, '');
+6 -10
View File
@@ -1,12 +1,8 @@
export const version = '2.13.0';
export const version = '2.13.2';
export const changeLogLink = 'https://t.me/jiao_long/303';
export const changeLog = `2.13.0 版本说明
1. 新增订阅管理模块,支持公开仓库/私有仓库等
2. 支持修改头像
3. 修复特殊字符环境变量异常
4. 修改定时任务添加标签
5. 修复添加任务默认会展示一个空标签
6. 修复用户名显示
7. 修复依赖安装
8. 其他bug修复
export const changeLog = `2.13.2 版本说明
1. 修复nodejs依赖安装失败pnpm setup
2. 修复订阅禁用失效
3. 修复新建订阅定时验证
4. 修复ssh配置文件更新逻辑
`;