mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-06 00:34:33 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bac306f9b3 | |||
| a6fd96e97b | |||
| 7edd91f923 | |||
| 66df445bd5 | |||
| 5c03034bb4 |
@@ -1 +1,7 @@
|
||||
export const LOG_END_SYMBOL = '\n ';
|
||||
|
||||
export const TASK_COMMAND = 'task';
|
||||
export const QL_COMMAND = 'ql';
|
||||
|
||||
export const TASK_PREFIX = `${TASK_COMMAND} `;
|
||||
export const QL_PREFIX = `${QL_COMMAND} `;
|
||||
|
||||
+6
-9
@@ -6,15 +6,12 @@ import config from './config';
|
||||
const app = express();
|
||||
|
||||
app.get('/api/public/panel/log', (req, res) => {
|
||||
exec(
|
||||
'pm2 logs panel --lines 500 --nostream --timestamp',
|
||||
(err, stdout, stderr) => {
|
||||
if (err || stderr) {
|
||||
return res.send({ code: 400, message: (err && err.message) || stderr });
|
||||
}
|
||||
return res.send({ code: 200, data: stdout });
|
||||
},
|
||||
);
|
||||
exec('tail -n 300 ~/.pm2/logs/panel-error.log', (err, stdout, stderr) => {
|
||||
if (err || stderr) {
|
||||
return res.send({ code: 400, message: (err && err.message) || stderr });
|
||||
}
|
||||
return res.send({ code: 200, data: stdout });
|
||||
});
|
||||
});
|
||||
|
||||
app
|
||||
|
||||
+6
-2
@@ -4,6 +4,7 @@ import { exec } from 'child_process';
|
||||
import Logger from './loaders/logger';
|
||||
import { CrontabModel, CrontabStatus } from './data/cron';
|
||||
import config from './config';
|
||||
import { QL_PREFIX, TASK_PREFIX } from './config/const';
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -23,8 +24,11 @@ const run = async () => {
|
||||
) {
|
||||
schedule.scheduleJob(task.schedule, function () {
|
||||
let command = task.command as string;
|
||||
if (!command.includes('task ') && !command.includes('ql ')) {
|
||||
command = `task ${command}`;
|
||||
if (
|
||||
!command.startsWith(TASK_PREFIX) &&
|
||||
!command.startsWith(QL_PREFIX)
|
||||
) {
|
||||
command = `${TASK_PREFIX}${command}`;
|
||||
}
|
||||
exec(`ID=${task.id} ${command}`);
|
||||
});
|
||||
|
||||
+16
-11
@@ -14,6 +14,7 @@ import {
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { Op, where, col as colFn } from 'sequelize';
|
||||
import path from 'path';
|
||||
import { TASK_PREFIX, QL_PREFIX } from '../config/const';
|
||||
|
||||
@Service()
|
||||
export default class CronService {
|
||||
@@ -31,7 +32,7 @@ export default class CronService {
|
||||
const tab = new Crontab(payload);
|
||||
tab.saved = false;
|
||||
const doc = await this.insert(tab);
|
||||
await this.set_crontab(this.isSixCron(doc));
|
||||
await this.set_crontab();
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ export default class CronService {
|
||||
public async update(payload: Crontab): Promise<Crontab> {
|
||||
payload.saved = false;
|
||||
const newDoc = await this.updateDb(payload);
|
||||
await this.set_crontab(this.isSixCron(newDoc));
|
||||
await this.set_crontab();
|
||||
return newDoc;
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ export default class CronService {
|
||||
|
||||
public async remove(ids: number[]) {
|
||||
await CrontabModel.destroy({ where: { id: ids } });
|
||||
await this.set_crontab(true);
|
||||
await this.set_crontab();
|
||||
}
|
||||
|
||||
public async pin(ids: number[]) {
|
||||
@@ -361,8 +362,8 @@ export default class CronService {
|
||||
this.logger.silly('Original command: ' + command);
|
||||
|
||||
let cmdStr = command;
|
||||
if (!cmdStr.includes('task ') && !cmdStr.includes('ql ')) {
|
||||
cmdStr = `task ${cmdStr}`;
|
||||
if (!cmdStr.startsWith(TASK_PREFIX) && !cmdStr.startsWith(QL_PREFIX)) {
|
||||
cmdStr = `${TASK_PREFIX}${cmdStr}`;
|
||||
}
|
||||
if (
|
||||
cmdStr.endsWith('.js') ||
|
||||
@@ -408,12 +409,12 @@ export default class CronService {
|
||||
|
||||
public async disabled(ids: number[]) {
|
||||
await CrontabModel.update({ isDisabled: 1 }, { where: { id: ids } });
|
||||
await this.set_crontab(true);
|
||||
await this.set_crontab();
|
||||
}
|
||||
|
||||
public async enabled(ids: number[]) {
|
||||
await CrontabModel.update({ isDisabled: 0 }, { where: { id: ids } });
|
||||
await this.set_crontab(true);
|
||||
await this.set_crontab();
|
||||
}
|
||||
|
||||
public async log(id: number) {
|
||||
@@ -519,11 +520,17 @@ export default class CronService {
|
||||
}
|
||||
|
||||
private make_command(tab: Crontab) {
|
||||
if (
|
||||
!tab.command.startsWith(TASK_PREFIX) &&
|
||||
!tab.command.startsWith(QL_PREFIX)
|
||||
) {
|
||||
tab.command = `${TASK_PREFIX}${tab.command}`;
|
||||
}
|
||||
const crontab_job_string = `ID=${tab.id} ${tab.command}`;
|
||||
return crontab_job_string;
|
||||
}
|
||||
|
||||
private async set_crontab(needReloadSchedule: boolean = false) {
|
||||
private async set_crontab() {
|
||||
const tabs = await this.crontabs();
|
||||
var crontab_string = '';
|
||||
tabs.data.forEach((tab) => {
|
||||
@@ -546,9 +553,7 @@ export default class CronService {
|
||||
fs.writeFileSync(config.crontabFile, crontab_string);
|
||||
|
||||
execSync(`crontab ${config.crontabFile}`);
|
||||
if (needReloadSchedule) {
|
||||
exec(`pm2 reload schedule`);
|
||||
}
|
||||
exec(`pm2 reload schedule`);
|
||||
await CrontabModel.update({ saved: true }, { where: {} });
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import SockService from './sock';
|
||||
import CronService from './cron';
|
||||
import ScheduleService, { TaskCallbacks } from './schedule';
|
||||
import config from '../config';
|
||||
import { LOG_END_SYMBOL } from '../config/const';
|
||||
import { TASK_COMMAND } from '../config/const';
|
||||
import { getPid, killTask } from '../config/util';
|
||||
|
||||
@Service()
|
||||
@@ -42,7 +42,7 @@ export default class ScriptService {
|
||||
|
||||
public async runScript(filePath: string) {
|
||||
const relativePath = path.relative(config.scriptPath, filePath);
|
||||
const command = `task -l ${relativePath} now`;
|
||||
const command = `${TASK_COMMAND} -l ${relativePath} now`;
|
||||
const pid = await this.scheduleService.runTask(
|
||||
command,
|
||||
this.taskCallbacks(filePath),
|
||||
@@ -56,7 +56,7 @@ export default class ScriptService {
|
||||
let str = '';
|
||||
if (!pid) {
|
||||
const relativePath = path.relative(config.scriptPath, filePath);
|
||||
pid = await getPid(`task -l ${relativePath} now`);
|
||||
pid = await getPid(`${TASK_COMMAND} -l ${relativePath} now`);
|
||||
}
|
||||
try {
|
||||
await killTask(pid);
|
||||
|
||||
Generated
+3320
-1926
File diff suppressed because it is too large
Load Diff
@@ -80,7 +80,7 @@ const CronModal = ({
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
autoSize={true}
|
||||
placeholder="请输入要执行的命令"
|
||||
placeholder="使用task命令运行脚本或其他任意LINUX可执行命令"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
||||
@@ -9,20 +9,37 @@ import './index.less';
|
||||
import { SharedContext } from '@/layouts';
|
||||
|
||||
const Error = () => {
|
||||
const { user, theme } = useOutletContext<SharedContext>();
|
||||
const { user, theme, reloadUser } = useOutletContext<SharedContext>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState('暂无日志');
|
||||
|
||||
const getLog = () => {
|
||||
setLoading(true);
|
||||
const getTimes = () => {
|
||||
return parseInt(localStorage.getItem('error_retry_times') || '0', 10);
|
||||
};
|
||||
|
||||
let times = getTimes();
|
||||
|
||||
const getLog = (needLoading: boolean = true) => {
|
||||
needLoading && setLoading(true);
|
||||
request
|
||||
.get(`${config.apiPrefix}public/panel/log`)
|
||||
.then(({ code, data }) => {
|
||||
if (code === 200) {
|
||||
setData(data);
|
||||
if (!data) {
|
||||
times = getTimes();
|
||||
if (times > 5) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('error_retry_times', `${times + 1}`);
|
||||
setTimeout(() => {
|
||||
reloadUser();
|
||||
getLog(false);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
.finally(() => needLoading && setLoading(false));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,7 +56,7 @@ const Error = () => {
|
||||
<div className="error-wrapper">
|
||||
{loading ? (
|
||||
<PageLoading />
|
||||
) : (
|
||||
) : data ? (
|
||||
<Terminal
|
||||
name="服务错误"
|
||||
colorMode={theme === 'vs-dark' ? ColorMode.Dark : ColorMode.Light}
|
||||
@@ -55,6 +72,10 @@ const Error = () => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : times > 5 ? (
|
||||
<>服务启动超时,请手动进入容器执行 ql -l check 后刷新再试</>
|
||||
) : (
|
||||
<PageLoading tip="启动中,请稍后..." />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ const EditModal = ({
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
if (!cNode || !cNode.title) {
|
||||
if (!cNode || !cNode.title || !currentPid) {
|
||||
return;
|
||||
}
|
||||
request
|
||||
|
||||
@@ -56,7 +56,6 @@ _request.interceptors.request.use((url, options) => {
|
||||
_request.interceptors.response.use(async (response) => {
|
||||
const responseStatus = response.status;
|
||||
if ([502, 504].includes(responseStatus)) {
|
||||
message.error('服务异常,请稍后刷新!');
|
||||
history.push('/error');
|
||||
} else if (responseStatus === 401) {
|
||||
if (history.location.pathname !== '/login') {
|
||||
|
||||
+6
-5
@@ -1,6 +1,7 @@
|
||||
version: 2.15.4
|
||||
changeLogLink: https://t.me/jiao_long/355
|
||||
version: 2.15.5
|
||||
changeLogLink: https://t.me/jiao_long/356
|
||||
changeLog: |
|
||||
1. 脚本管理支持重命名文件/文件夹
|
||||
2. 关于增加更新日志查看
|
||||
3. 修复定时任务脚本识别和跳转
|
||||
1. 修复定时任务命令不以task开头时,不能自动执行
|
||||
2. 修改服务异常逻辑
|
||||
3. 修复退出调试报错
|
||||
4. 修复脚本编辑器加载慢
|
||||
|
||||
Reference in New Issue
Block a user