Merge branch 'whyour-develop' into develop

This commit is contained in:
Ahaochan 2023-08-14 11:40:38 +08:00
commit 7115b8e6a1
45 changed files with 285 additions and 196 deletions

View File

@ -13,9 +13,6 @@ export default defineConfig({
outputPath: 'static/dist', outputPath: 'static/dist',
fastRefresh: true, fastRefresh: true,
favicons: [`https://qn.whyour.cn/favicon.svg`], favicons: [`https://qn.whyour.cn/favicon.svg`],
mfsu: {
strategy: 'eager',
},
publicPath: process.env.NODE_ENV === 'production' ? './' : '/', publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
proxy: { proxy: {
[`${baseUrl}api/public`]: { [`${baseUrl}api/public`]: {

View File

@ -387,28 +387,22 @@ export function emptyDir(path: string) {
fs.rmdirSync(path); fs.rmdirSync(path);
} }
export function promiseExec(command: string): Promise<string> { export async function promiseExec(command: string): Promise<string> {
return new Promise((resolve, reject) => { try {
exec( const { stderr, stdout } = await promisify(exec)(command, { maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' });
command, return stdout || stderr;
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' }, } catch (error) {
(err, stdout, stderr) => { return JSON.stringify(error);
resolve(stdout || stderr || JSON.stringify(err)); }
},
);
});
} }
export function promiseExecSuccess(command: string): Promise<string> { export async function promiseExecSuccess(command: string): Promise<string> {
return new Promise((resolve) => { try {
exec( const { stdout } = await promisify(exec)(command, { maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' });
command, return stdout || '';
{ maxBuffer: 200 * 1024 * 1024, encoding: 'utf8' }, } catch (error) {
(err, stdout, stderr) => { return '';
resolve(stdout || ''); }
},
);
});
} }
export function parseHeaders(headers: string) { export function parseHeaders(headers: string) {
@ -501,7 +495,7 @@ export async function killTask(pid: number) {
[pid, ...pids].forEach((x) => { [pid, ...pids].forEach((x) => {
process.kill(x, 2); process.kill(x, 2);
}); });
} catch (error) {} } catch (error) { }
} else { } else {
process.kill(pid, 2); process.kill(pid, 2);
} }

View File

@ -7,18 +7,23 @@ import linkDeps from './deps';
import initTask from './initTask'; import initTask from './initTask';
export default async ({ expressApp }: { expressApp: Application }) => { export default async ({ expressApp }: { expressApp: Application }) => {
await depInjectorLoader(); try {
Logger.info('✌️ Dependency Injector loaded'); depInjectorLoader();
Logger.info('✌️ Dependency Injector loaded');
await expressLoader({ app: expressApp });
Logger.info('✌️ Express loaded'); expressLoader({ app: expressApp });
Logger.info('✌️ Express loaded');
await initData();
Logger.info('✌️ init data loaded'); await initData();
Logger.info('✌️ init data loaded');
await linkDeps();
Logger.info('✌️ link deps loaded'); await linkDeps();
Logger.info('✌️ link deps loaded');
initTask();
Logger.info('✌️ init task loaded'); initTask();
Logger.info('✌️ init task loaded');
} catch (error) {
Logger.info('✌️ depInjectorLoader expressLoader initData linkDeps failed');
Logger.error(error);
}
}; };

View File

@ -125,6 +125,6 @@ export default async () => {
Logger.info('✌️ DB loaded'); Logger.info('✌️ DB loaded');
} catch (error) { } catch (error) {
Logger.info('✌️ DB load failed'); Logger.info('✌️ DB load failed');
Logger.info(error); Logger.error(error);
} }
}; };

View File

@ -37,7 +37,7 @@ async function linkCommand() {
if (fs.existsSync(target)) { if (fs.existsSync(target)) {
fs.unlinkSync(target); fs.unlinkSync(target);
} }
fs.symlink(source, target, (err) => {}); fs.symlink(source, target, (err) => { });
} }
} }

View File

@ -24,7 +24,7 @@ const check = async (
`tail -n 300 ~/.pm2/logs/schedule-error.log`, `tail -n 300 ~/.pm2/logs/schedule-error.log`,
); );
return callback( return callback(
new Error(`${scheduleErrLog || ''}\n${panelErrLog || ''}\n${res}`), new Error(`${scheduleErrLog || ''}\n${panelErrLog || ''}\n${res}`.trim()),
); );
default: default:

View File

@ -161,7 +161,7 @@ export default class CronService {
case 'In': case 'In':
q[Op.or] = [ q[Op.or] = [
{ {
[property]: value, [property]: Array.isArray(value) ? value : [value],
}, },
property === 'status' && value.includes(2) property === 'status' && value.includes(2)
? { isDisabled: 1 } ? { isDisabled: 1 }
@ -172,7 +172,7 @@ export default class CronService {
q[Op.and] = [ q[Op.and] = [
{ {
[property]: { [property]: {
[Op.notIn]: value, [Op.notIn]: Array.isArray(value) ? value : [value],
}, },
}, },
property === 'status' && value.includes(2) property === 'status' && value.includes(2)

View File

@ -458,7 +458,7 @@ class WeCom:
return respone["errmsg"] return respone["errmsg"]
def send_mpnews(self, title, message, media_id, touser="@all"): def send_mpnews(self, title, message, media_id, touser="@all"):
send_url = f"https://{self.HOST}/cgi-bin/message/send?access_token={self.get_access_token()}" send_url = f"https://{self.ORIGIN}/cgi-bin/message/send?access_token={self.get_access_token()}"
send_values = { send_values = {
"touser": touser, "touser": touser,
"msgtype": "mpnews", "msgtype": "mpnews",

View File

@ -14,7 +14,7 @@ single_hanle() {
## 选择python3还是node ## 选择python3还是node
define_program() { define_program() {
local file_param=$1 local file_param=$1
if [[ $file_param == *.js ]]; then if [[ $file_param == *.js ]] || [[ $file_param == *.mjs ]]; then
which_program="node" which_program="node"
elif [[ $file_param == *.py ]] || [[ $file_param == *.pyc ]]; then elif [[ $file_param == *.py ]] || [[ $file_param == *.pyc ]]; then
which_program="python3" which_program="python3"

View File

@ -3,13 +3,17 @@ import intl from 'react-intl-universal';
export function rootContainer(container: any) { export function rootContainer(container: any) {
const locales = { const locales = {
'en-US': require('./locales/en-US.json'), 'en': require('./locales/en-US.json'),
'zh-CN': require('./locales/zh-CN.json'), 'zh': require('./locales/zh-CN.json'),
}; };
let currentLocale = intl.determineLocale({ let currentLocale = intl.determineLocale({
urlLocaleKey: 'lang', urlLocaleKey: 'lang',
cookieLocaleKey: 'lang', cookieLocaleKey: 'lang',
}); }).slice(0, 2);
if (!currentLocale || !Object.keys(locales).includes(currentLocale)) {
currentLocale = 'zh';
}
intl.init({ currentLocale, locales }); intl.init({ currentLocale, locales });
return container; return container;

View File

@ -13,7 +13,7 @@ export default function Name<
options: Options<TData, [TParams]>; options: Options<TData, [TParams]>;
}) { }) {
const { loading, data } = useRequest(service, options); const { loading, data } = useRequest(service, options);
console.log(loading, data);
return ( return (
<Spin spinning={loading}> <Spin spinning={loading}>
<Typography.Text ellipsis={true}>{data?.data?.name}</Typography.Text> <Typography.Text ellipsis={true}>{data?.data?.name}</Typography.Text>

View File

@ -396,16 +396,57 @@
"关联订阅": "Associate Subscription", "关联订阅": "Associate Subscription",
"订阅": "Subscription", "订阅": "Subscription",
"创建": "Create", "创建": "Create",
"创建订阅成功": "Subscription created successfully",
"gotify的url地址,例如 https://push.example.de:8080": "Gotify URL address, e.g., https://push.example.de:8080",
"BARK推送图标,自定义推送图标 (需iOS15或以上才能显示)": "BARK push icon, custom push icon (requires iOS 15 or above to display)",
"BARK推送铃声,铃声列表去APP查看复制填写": "BARK push ringtone, check the ringtone list in the APP and copy it",
"BARK推送消息的分组, 默认为qinglong": "BARK push message grouping, default is qinglong",
"telegram代理配置认证参数, 用户名与密码用英文冒号连接 user:password": "Telegram proxy configuration authentication parameters, connect username and password with a colon, e.g., user:password",
"企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770)例如693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa": "WeChat Work Bot webhook (see documentation at https://work.weixin.qq.com/api/doc/90000/90136/91770), e.g., 693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa",
"corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型) 注意用,号隔开(英文输入法的逗号)例如wwcfrs,B-76WERQ,qinglong,1000001,2COat": "corpid, corpsecret, touser (note: separate multiple member IDs with |), agentid, message type (optional, defaults to text message type) separated by commas (`,`), e.g., wwcfrs, B-76WERQ, qinglong, 1000001, 2COat",
"密钥key,智能微秘书个人中心获取apikey申请地址https://wechat.aibotk.com/signup?from=ql": "Key, obtain the API key from the Smart WeChat Assistant's personal center, apply at: https://wechat.aibotk.com/signup?from=ql",
"一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "The 'Group Code' for one-to-many push (under one-to-many push->Your Group (create one if none exists)->Group Code. If you are the creator of the group, you also need to click 'View QR Code' for scanning and binding, otherwise you cannot receive group message push)",
"同时删除关联任务和脚本": "Delete associated tasks and scripts as well", "同时删除关联任务和脚本": "Delete associated tasks and scripts as well",
"夹及其子文件": "Folder and its sub-files" "未找到": "Not Found",
"保存成功": "Saved successfully",
"删除成功": "Deleted successfully",
"批量删除成功": "Batch deleted successfully",
"启动中...": "Starting...",
"任务未运行": "Task not running",
"更新任务成功": "Task updated successfully",
"创建任务成功": "Task created successfully",
"编辑任务": "Edit Task",
"Cron表达式格式有误": "Incorrect Cron Expression Format",
"添加Labels成功": "Labels added successfully",
"删除Labels成功": "Labels deleted successfully",
"编辑视图": "Edit View",
"排序方式": "Sort Order",
"开始时间": "Start Time",
"安装": "Install",
"结束时间": "End Time",
"编辑依赖": "Edit Dependency",
"更新环境变量名称成功": "Environment Variable Name updated successfully",
"更新变量成功": "Variable updated successfully",
"创建变量成功": "Variable created successfully",
"编辑变量": "Edit Variable",
"加载中...": "Loading...",
"夹下所以日志": "Folder and All Subfiles",
"创建文件夹成功": "Folder created successfully",
"创建文件成功": "File created successfully",
"夹及其子文件": "Folder and Its Subfiles",
"更新名称成功": "Name updated successfully",
"保存文件成功": "File saved successfully",
"更新应用成功": "Application updated successfully",
"创建应用成功": "Application created successfully",
"编辑应用": "Edit Application",
"检查更新中...": "Checking for updates...",
"失败,请检查": "Failed, please check",
"更新失败,请检查网络及日志或稍后再试": "Update failed, please check network and logs or try again later",
"更新包下载成功": "Update package download successful",
"重置secret": "Reset secret",
"重置成功": "Reset successful",
"通知发送成功": "Notification sent successfully",
"通知关闭成功": "Notification closed successfully",
"测试中...": "Testing...",
"上传": "Upload",
"下载": "Download",
"更新成功": "Update successful",
"激活成功": "Activation successful",
"验证失败": "Validation failed",
"更新订阅成功": "Subscription updated successfully",
"创建订阅成功": "Subscription created successfully",
"编辑订阅": "Edit Subscription",
"Subscription表达式格式有误": "Incorrect Subscription Expression Format",
"一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "The 'Group Code' for One-to-Many Push (Below One-to-Many Push->Your Group (if not, create one)->Group Code, if you are the creator of the group, you also need to click 'View QR Code' to scan and bind, otherwise you cannot receive group messages)",
"登录已过期,请重新登录": "Login session has expired, please log in again"
} }

View File

@ -147,7 +147,7 @@
"启动中,请稍后...": "启动中,请稍后...", "启动中,请稍后...": "启动中,请稍后...",
"欢迎使用": "欢迎使用", "欢迎使用": "欢迎使用",
"欢迎使用青龙": "欢迎使用青龙", "欢迎使用青龙": "欢迎使用青龙",
"支持python3、javascript、shell、typescript 的定时任务管理面板": "支持python3、javaScript、shell、typescript 的定时任务管理面板", "支持python3、javascript、shell、typescript 的定时任务管理面板": "支持python3、javascript、shell、typescript 的定时任务管理面板",
"开始安装": "开始安装", "开始安装": "开始安装",
"账户设置": "账户设置", "账户设置": "账户设置",
"用户名": "用户名", "用户名": "用户名",
@ -396,5 +396,57 @@
"关联订阅": "关联订阅", "关联订阅": "关联订阅",
"订阅": "订阅", "订阅": "订阅",
"创建": "创建", "创建": "创建",
"同时删除关联任务和脚本": "同时删除关联任务和脚本" "同时删除关联任务和脚本": "同时删除关联任务和脚本",
"未找到": "未找到",
"保存成功": "保存成功",
"删除成功": "删除成功",
"批量删除成功": "批量删除成功",
"启动中...": "启动中...",
"任务未运行": "任务未运行",
"更新任务成功": "更新任务成功",
"创建任务成功": "创建任务成功",
"编辑任务": "编辑任务",
"Cron表达式格式有误": "Cron表达式格式有误",
"添加Labels成功": "添加Labels成功",
"删除Labels成功": "删除Labels成功",
"编辑视图": "编辑视图",
"排序方式": "排序方式",
"开始时间": "开始时间",
"安装": "安装",
"结束时间": "结束时间",
"编辑依赖": "编辑依赖",
"更新环境变量名称成功": "更新环境变量名称成功",
"更新变量成功": "更新变量成功",
"创建变量成功": "创建变量成功",
"编辑变量": "编辑变量",
"加载中...": "加载中...",
"夹下所以日志": "夹下所以日志",
"创建文件夹成功": "创建文件夹成功",
"创建文件成功": "创建文件成功",
"夹及其子文件": "夹及其子文件",
"更新名称成功": "更新名称成功",
"保存文件成功": "保存文件成功",
"更新应用成功": "更新应用成功",
"创建应用成功": "创建应用成功",
"编辑应用": "编辑应用",
"检查更新中...": "检查更新中...",
"失败,请检查": "失败,请检查",
"更新失败,请检查网络及日志或稍后再试": "更新失败,请检查网络及日志或稍后再试",
"更新包下载成功": "更新包下载成功",
"重置secret": "重置secret",
"重置成功": "重置成功",
"通知发送成功": "通知发送成功",
"通知关闭成功": "通知关闭成功",
"测试中...": "测试中...",
"上传": "上传",
"下载": "下载",
"更新成功": "更新成功",
"激活成功": "激活成功",
"验证失败": "验证失败",
"更新订阅成功": "更新订阅成功",
"创建订阅成功": "创建订阅成功",
"编辑订阅": "编辑订阅",
"Subscription表达式格式有误": "Subscription表达式格式有误",
"一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)",
"登录已过期,请重新登录": "登录已过期,请重新登录"
} }

View File

@ -55,7 +55,7 @@ const Config = () => {
.post(`${config.apiPrefix}configs/save`, { content, name: select }) .post(`${config.apiPrefix}configs/save`, { content, name: select })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存成功'); message.success(intl.get('保存成功'));
} }
setConfirmLoading(false); setConfirmLoading(false);
}); });

View File

@ -30,7 +30,7 @@ import config from '@/utils/config';
import CronLogModal from './logModal'; import CronLogModal from './logModal';
import Editor from '@monaco-editor/react'; import Editor from '@monaco-editor/react';
import IconFont from '@/components/iconfont'; import IconFont from '@/components/iconfont';
import { getCommandScript } from '@/utils'; import { getCommandScript, getEditorMode } from '@/utils';
import VirtualList from 'rc-virtual-list'; import VirtualList from 'rc-virtual-list';
import useScrollHeight from '@/hooks/useScrollHeight'; import useScrollHeight from '@/hooks/useScrollHeight';
@ -46,12 +46,6 @@ const tabList = [
tab: intl.get('脚本'), tab: intl.get('脚本'),
}, },
]; ];
const LangMap: any = {
'.py': 'python',
'.js': 'javascript',
'.sh': 'shell',
'.ts': 'typescript',
};
interface LogItem { interface LogItem {
directory: string; directory: string;
@ -109,7 +103,7 @@ const CronDetailModal = ({
), ),
script: scriptInfo.filename && ( script: scriptInfo.filename && (
<Editor <Editor
language={LangMap[scriptInfo.filename.slice(-3)] || ''} language={getEditorMode(scriptInfo.filename)}
theme={theme} theme={theme}
value={value} value={value}
options={{ options={{

View File

@ -447,7 +447,7 @@ const Crontab = () => {
.delete(`${config.apiPrefix}crons`, { data: [record.id] }) .delete(`${config.apiPrefix}crons`, { data: [record.id] })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('删除成功'); message.success(intl.get('删除成功'));
const result = [...value]; const result = [...value];
const i = result.findIndex((x) => x.id === record.id); const i = result.findIndex((x) => x.id === record.id);
if (i !== -1) { if (i !== -1) {
@ -729,7 +729,7 @@ const Crontab = () => {
.delete(`${config.apiPrefix}crons`, { data: selectedRowIds }) .delete(`${config.apiPrefix}crons`, { data: selectedRowIds })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('批量删除成功'); message.success(intl.get('批量删除成功'));
setSelectedRowIds([]); setSelectedRowIds([]);
getCrons(); getCrons();
} }
@ -905,6 +905,11 @@ const Crontab = () => {
setViewConf(view ? view : null); setViewConf(view ? view : null);
}; };
const [vt] = useVT(
() => ({ scroll: { y: tableScrollHeight } }),
[tableScrollHeight],
);
return ( return (
<PageContainer <PageContainer
className="ql-container-wrapper crontab-wrapper ql-container-wrapper-has-tab" className="ql-container-wrapper crontab-wrapper ql-container-wrapper-has-tab"
@ -1041,6 +1046,7 @@ const Crontab = () => {
rowSelection={rowSelection} rowSelection={rowSelection}
rowClassName={getRowClassName} rowClassName={getRowClassName}
onChange={onPageChange} onChange={onPageChange}
components={isPhone || pageConf.size < 50 ? undefined : vt}
/> />
</div> </div>
<CronLogModal <CronLogModal

View File

@ -26,7 +26,7 @@ const CronLogModal = ({
data?: string; data?: string;
logUrl?: string; logUrl?: string;
}) => { }) => {
const [value, setValue] = useState<string>('启动中...'); const [value, setValue] = useState<string>(intl.get('启动中...'));
const [loading, setLoading] = useState<any>(true); const [loading, setLoading] = useState<any>(true);
const [executing, setExecuting] = useState<any>(true); const [executing, setExecuting] = useState<any>(true);
const [isPhone, setIsPhone] = useState(false); const [isPhone, setIsPhone] = useState(false);

View File

@ -99,7 +99,7 @@ const CronModal = ({
if (!value || cronParse.parseExpression(value).hasNext()) { if (!value || cronParse.parseExpression(value).hasNext()) {
return Promise.resolve(); return Promise.resolve();
} else { } else {
return Promise.reject('Cron表达式格式有误'); return Promise.reject(intl.get('Cron表达式格式有误'));
} }
}, },
}, },

View File

@ -157,7 +157,7 @@ const ViewManageModal = ({
.delete(`${config.apiPrefix}crons/views`, { data: [record.id] }) .delete(`${config.apiPrefix}crons/views`, { data: [record.id] })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('删除成功'); message.success(intl.get('删除成功'));
cronViewChange(); cronViewChange();
} }
}); });

View File

@ -400,7 +400,7 @@ const Dependence = () => {
const { type, message, references } = socketMessage; const { type, message, references } = socketMessage;
if ( if (
type === 'installDependence' && type === 'installDependence' &&
message.includes('开始时间') && message.includes(intl.get('开始时间')) &&
references.length > 0 references.length > 0
) { ) {
const result = [...value]; const result = [...value];
@ -409,7 +409,7 @@ const Dependence = () => {
if (index !== -1) { if (index !== -1) {
result.splice(index, 1, { result.splice(index, 1, {
...value[index], ...value[index],
status: message.includes('安装') ? Status.安装中 : Status.删除中, status: message.includes(intl.get('安装')) ? Status.安装中 : Status.删除中,
}); });
} }
} }
@ -417,14 +417,14 @@ const Dependence = () => {
} }
if ( if (
type === 'installDependence' && type === 'installDependence' &&
message.includes('结束时间') && message.includes(intl.get('结束时间')) &&
references.length > 0 references.length > 0
) { ) {
let status; let status;
if (message.includes('安装')) { if (message.includes(intl.get('安装'))) {
status = message.includes('成功') ? Status.已安装 : Status.安装失败; status = message.includes(intl.get('成功')) ? Status.已安装 : Status.安装失败;
} else { } else {
status = message.includes('成功') ? Status.已删除 : Status.删除失败; status = message.includes(intl.get('成功')) ? Status.已删除 : Status.删除失败;
} }
const result = [...value]; const result = [...value];
for (let i = 0; i < references.length; i++) { for (let i = 0; i < references.length; i++) {

View File

@ -56,8 +56,8 @@ const DependenceLogModal = ({
) { ) {
const log = (data.log || []).join('') as string; const log = (data.log || []).join('') as string;
setValue(log); setValue(log);
setExecuting(!log.includes('结束时间')); setExecuting(!log.includes(intl.get('结束时间')));
setIsRemoveFailed(log.includes('删除失败')); setIsRemoveFailed(log.includes(intl.get('删除失败')));
} }
}) })
.finally(() => { .finally(() => {
@ -103,9 +103,9 @@ const DependenceLogModal = ({
references.length > 0 && references.length > 0 &&
references.includes(dependence.id) references.includes(dependence.id)
) { ) {
if (message.includes('结束时间')) { if (message.includes(intl.get('结束时间'))) {
setExecuting(false); setExecuting(false);
setIsRemoveFailed(message.includes('删除失败')); setIsRemoveFailed(message.includes(intl.get('删除失败')));
} }
setValue(`${value}${message}`); setValue(`${value}${message}`);
} }

View File

@ -54,7 +54,7 @@ const Diff = () => {
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存成功'); message.success(intl.get('保存成功'));
} }
}); });
}; };

View File

@ -25,7 +25,7 @@ const EditNameModal = ({
}); });
if (code === 200) { if (code === 200) {
message.success('更新环境变量名称成功'); message.success(intl.get('更新环境变量名称成功'));
handleCancel(); handleCancel();
} }
setLoading(false); setLoading(false);

View File

@ -254,7 +254,7 @@ const Env = () => {
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success( message.success(
`${record.status === Status. ? intl.get('启用') : intl.get('禁用')}成功`, `${record.status === Status. ? intl.get('启用') : intl.get('禁用')}${intl.get('成功')}`,
); );
const newStatus = const newStatus =
record.status === Status. ? Status.已启用 : Status.已禁用; record.status === Status. ? Status.已启用 : Status.已禁用;
@ -300,7 +300,7 @@ const Env = () => {
.delete(`${config.apiPrefix}envs`, { data: [record.id] }) .delete(`${config.apiPrefix}envs`, { data: [record.id] })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('删除成功'); message.success(intl.get('删除成功'));
const result = [...value]; const result = [...value];
result.splice(index, 1); result.splice(index, 1);
setValue(result); setValue(result);
@ -420,7 +420,7 @@ const Env = () => {
.delete(`${config.apiPrefix}envs`, { data: selectedRowIds }) .delete(`${config.apiPrefix}envs`, { data: selectedRowIds })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('批量删除成功'); message.success(intl.get('批量删除成功'));
setSelectedRowIds([]); setSelectedRowIds([]);
getEnvs(); getEnvs();
} }

View File

@ -9,17 +9,22 @@ import { SharedContext } from '@/layouts';
import { Alert, Typography } from 'antd'; import { Alert, Typography } from 'antd';
const Error = () => { const Error = () => {
const { user, theme, reloadUser } = useOutletContext<SharedContext>(); const { user } = useOutletContext<SharedContext>();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [data, setData] = useState('暂无日志'); const [data, setData] = useState(intl.get('暂无日志'));
const retryTimes = useRef(1); const retryTimes = useRef(1);
const getLog = (needLoading: boolean = true) => { const getHealthStatus = (needLoading: boolean = true) => {
needLoading && setLoading(true); needLoading && setLoading(true);
request request
.get(`${config.apiPrefix}public/health`) .get(`${config.apiPrefix}public/health`)
.then(({ error, data }) => { .then(({ error, data }) => {
if (data?.status === 1) { if (data?.status === 1) {
if (retryTimes.current > 1) {
setTimeout(() => {
window.location.reload();
});
}
return; return;
} }
if (retryTimes.current > 3) { if (retryTimes.current > 3) {
@ -28,7 +33,7 @@ const Error = () => {
} }
retryTimes.current += 1; retryTimes.current += 1;
setTimeout(() => { setTimeout(() => {
getLog(false); getHealthStatus(false);
}, 3000); }, 3000);
}) })
.finally(() => needLoading && setLoading(false)); .finally(() => needLoading && setLoading(false));
@ -41,7 +46,7 @@ const Error = () => {
}, [user]); }, [user]);
useEffect(() => { useEffect(() => {
getLog(); getHealthStatus();
}, []); }, []);
return ( return (
@ -69,7 +74,7 @@ const Error = () => {
<div>{intl.get('2. 容器内执行 ql -l check、ql -l update')}</div> <div>{intl.get('2. 容器内执行 ql -l check、ql -l update')}</div>
<div> <div>
{intl.get( {intl.get(
'3. 如果无法解决,容器内执行 pm2 logs拷贝执行结果', '3. 如果无法解决,容器内执行 pm2 logs拷贝执行结果'
)} )}
<Typography.Link href="https://github.com/whyour/qinglong/issues/new?assignees=&labels=&template=bug_report.yml"> <Typography.Link href="https://github.com/whyour/qinglong/issues/new?assignees=&labels=&template=bug_report.yml">
{intl.get('提交 issue')} {intl.get('提交 issue')}

View File

@ -75,7 +75,7 @@ const Log = () => {
return; return;
} }
setValue('加载中...'); setValue(intl.get('加载中...'));
getLog(node); getLog(node);
}; };

View File

@ -187,7 +187,9 @@ const Login = () => {
<Form layout="vertical" onFinish={handleOk}> <Form layout="vertical" onFinish={handleOk}>
<FormItem name="username" label={intl.get('用户名')} hasFeedback> <FormItem name="username" label={intl.get('用户名')} hasFeedback>
<Input <Input
placeholder={`${intl.get('用户名')}${isDemoEnv ? ': admin' : ''}`} placeholder={`${intl.get('用户名')}${
isDemoEnv ? ': admin' : ''
}`}
autoFocus autoFocus
/> />
</FormItem> </FormItem>

View File

@ -8,21 +8,9 @@ import Editor from '@monaco-editor/react';
import SaveModal from './saveModal'; import SaveModal from './saveModal';
import SettingModal from './setting'; import SettingModal from './setting';
import { useTheme } from '@/utils/hooks'; import { useTheme } from '@/utils/hooks';
import { logEnded } from '@/utils'; import { getEditorMode, logEnded } from '@/utils';
const { Option } = Select; const { Option } = Select;
const LangMap: any = {
'.py': 'python',
'.js': 'javascript',
'.sh': 'shell',
'.ts': 'typescript',
};
const prefixMap: any = {
python: '.py',
javascript: '.js',
shell: '.sh',
typescript: '.ts',
};
const EditModal = ({ const EditModal = ({
treeData, treeData,
@ -65,7 +53,7 @@ const EditModal = ({
return; return;
} }
const newMode = LangMap[value.slice(-3)] || ''; const newMode = getEditorMode(value);
setCNode(node); setCNode(node);
setLanguage(newMode); setLanguage(newMode);
getDetail(node); getDetail(node);
@ -145,7 +133,7 @@ const EditModal = ({
setCNode(currentNode); setCNode(currentNode);
setValue(content as string); setValue(content as string);
setSelectedKey(currentNode.key); setSelectedKey(currentNode.key);
const newMode = LangMap[currentNode.title.slice(-3)] || ''; const newMode = getEditorMode(currentNode.title);
setLanguage(newMode); setLanguage(newMode);
} }
}, [content, currentNode]); }, [content, currentNode]);

View File

@ -37,7 +37,7 @@ import EditScriptNameModal from './editNameModal';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { history, useOutletContext, useLocation } from '@umijs/max'; import { history, useOutletContext, useLocation } from '@umijs/max';
import { parse } from 'query-string'; import { parse } from 'query-string';
import { depthFirstSearch, findNode } from '@/utils'; import { depthFirstSearch, findNode, getEditorMode } from '@/utils';
import { SharedContext } from '@/layouts'; import { SharedContext } from '@/layouts';
import useFilterTreeData from '@/hooks/useFilterTreeData'; import useFilterTreeData from '@/hooks/useFilterTreeData';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
@ -46,13 +46,6 @@ import RenameModal from './renameModal';
const { Text } = Typography; const { Text } = Typography;
const LangMap: any = {
'.py': 'python',
'.js': 'javascript',
'.sh': 'shell',
'.ts': 'typescript',
};
const Script = () => { const Script = () => {
const { headerStyle, isPhone, theme, socketMessage } = const { headerStyle, isPhone, theme, socketMessage } =
useOutletContext<SharedContext>(); useOutletContext<SharedContext>();
@ -133,9 +126,9 @@ const Script = () => {
return; return;
} }
const newMode = value ? LangMap[value.slice(-3)] : ''; const newMode = getEditorMode(value);
setMode(isPhone && newMode === 'typescript' ? 'javascript' : newMode); setMode(isPhone && newMode === 'typescript' ? 'javascript' : newMode);
setValue('加载中...'); setValue(intl.get('加载中...'));
getDetail(node); getDetail(node);
}; };
@ -201,7 +194,7 @@ const Script = () => {
const cancelEdit = () => { const cancelEdit = () => {
setIsEditing(false); setIsEditing(false);
setValue('加载中...'); setValue(intl.get('加载中...'));
getDetail(currentNode); getDetail(currentNode);
}; };

View File

@ -29,7 +29,7 @@ const RenameModal = ({
); );
if (code === 200) { if (code === 200) {
message.success('更新名称成功'); message.success(intl.get('更新名称成功'));
handleCancel(); handleCancel();
} }
setLoading(false); setLoading(false);

View File

@ -23,7 +23,7 @@ const SaveModal = ({
.post(`${config.apiPrefix}scripts`, payload) .post(`${config.apiPrefix}scripts`, payload)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success(intl.get('保存文件成功'));
handleCancel(data); handleCancel(data);
} }
setLoading(false); setLoading(false);

View File

@ -23,7 +23,7 @@ const SettingModal = ({
.post(`${config.apiPrefix}scripts`, payload) .post(`${config.apiPrefix}scripts`, payload)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('保存文件成功'); message.success(intl.get('保存文件成功'));
handleCancel(data); handleCancel(data);
} }
setLoading(false); setLoading(false);

View File

@ -10,6 +10,7 @@ const { Link } = Typography;
enum TVersion { enum TVersion {
'develop' = '开发版', 'develop' = '开发版',
'master' = '正式版', 'master' = '正式版',
'debian' = '正式版'
} }
const About = ({ systemInfo }: { systemInfo: SharedContext['systemInfo'] }) => { const About = ({ systemInfo }: { systemInfo: SharedContext['systemInfo'] }) => {

View File

@ -14,7 +14,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
const checkUpgrade = () => { const checkUpgrade = () => {
if (updateLoading) return; if (updateLoading) return;
setUpdateLoading(true); setUpdateLoading(true);
message.loading('检查更新中...', 0); message.loading(intl.get('检查更新中...'), 0);
request request
.put(`${config.apiPrefix}system/update-check`) .put(`${config.apiPrefix}system/update-check`)
.then(({ code, data }) => { .then(({ code, data }) => {
@ -177,7 +177,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
} }
const newMessage = `${value}${_message}`; const newMessage = `${value}${_message}`;
const updateFailed = newMessage.includes('失败'); const updateFailed = newMessage.includes(intl.get('失败'));
modalRef.current.update({ modalRef.current.update({
maskClosable: updateFailed, maskClosable: updateFailed,
@ -198,8 +198,8 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
), ),
}); });
if (updateFailed && !value.includes('失败,请检查')) { if (updateFailed && !value.includes(intl.get('失败,请检查'))) {
message.error('更新失败,请检查网络及日志或稍后再试'); message.error(intl.get('更新失败,请检查网络及日志或稍后再试'));
} }
setValue(newMessage); setValue(newMessage);
@ -209,7 +209,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
.getElementById('log-identifier')! .getElementById('log-identifier')!
.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); .scrollIntoView({ behavior: 'smooth', block: 'nearest' });
if (_message.includes('更新包下载成功')) { if (_message.includes(intl.get('更新包下载成功'))) {
setTimeout(() => { setTimeout(() => {
showReloadModal(); showReloadModal();
}, 1000); }, 1000);

View File

@ -154,7 +154,7 @@ const Setting = () => {
.delete(`${config.apiPrefix}apps`, { data: [record.id] }) .delete(`${config.apiPrefix}apps`, { data: [record.id] })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('删除成功'); message.success(intl.get('删除成功'));
const result = [...dataSource]; const result = [...dataSource];
result.splice(index, 1); result.splice(index, 1);
setDataSource(result); setDataSource(result);
@ -188,7 +188,7 @@ const Setting = () => {
.put(`${config.apiPrefix}apps/${record.id}/reset-secret`) .put(`${config.apiPrefix}apps/${record.id}/reset-secret`)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('重置成功'); message.success(intl.get('重置成功'));
handleApp(data); handleApp(data);
} }
}); });

View File

@ -41,8 +41,8 @@ const Other = ({
const [form] = Form.useForm(); const [form] = Form.useForm();
const modalRef = useRef<any>(); const modalRef = useRef<any>();
const [exportLoading, setExportLoading] = useState(false); const [exportLoading, setExportLoading] = useState(false);
const showUploadProgress = useProgress('上传'); const showUploadProgress = useProgress(intl.get('上传'));
const showDownloadProgress = useProgress('下载'); const showDownloadProgress = useProgress(intl.get('下载'));
const { const {
enable: enableDarkMode, enable: enableDarkMode,
@ -85,7 +85,7 @@ const Other = ({
.put(`${config.apiPrefix}system/config`, systemConfig) .put(`${config.apiPrefix}system/config`, systemConfig)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('更新成功'); message.success(intl.get('更新成功'));
} }
}) })
.catch((error: any) => { .catch((error: any) => {
@ -162,7 +162,7 @@ const Other = ({
return ( return (
<Form layout="vertical" form={form}> <Form layout="vertical" form={form}>
<Form.Item <Form.Item
label={intl.get('主题设置')} label={intl.get('主题')}
name="theme" name="theme"
initialValue={defaultTheme} initialValue={defaultTheme}
> >

View File

@ -67,12 +67,12 @@ const SecuritySettings = ({ user, userChange }: any) => {
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
if (data) { if (data) {
message.success('激活成功'); message.success(intl.get('激活成功'));
setTwoFactoring(false); setTwoFactoring(false);
setTwoFactorActivated(true); setTwoFactorActivated(true);
userChange(); userChange();
} else { } else {
message.success('验证失败'); message.success(intl.get('验证失败'));
} }
} }
}) })

View File

@ -366,7 +366,7 @@ const Subscription = () => {
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success('删除成功'); message.success(intl.get('删除成功'));
const result = [...value]; const result = [...value];
const i = result.findIndex((x) => x.id === record.id); const i = result.findIndex((x) => x.id === record.id);
if (i !== -1) { if (i !== -1) {

View File

@ -23,7 +23,7 @@ const SubscriptionLogModal = ({
data?: string; data?: string;
logUrl?: string; logUrl?: string;
}) => { }) => {
const [value, setValue] = useState<string>('启动中...'); const [value, setValue] = useState<string>(intl.get('启动中...'));
const [loading, setLoading] = useState<any>(true); const [loading, setLoading] = useState<any>(true);
const [executing, setExecuting] = useState<any>(true); const [executing, setExecuting] = useState<any>(true);
const [isPhone, setIsPhone] = useState(false); const [isPhone, setIsPhone] = useState(false);

View File

@ -392,7 +392,7 @@ const SubscriptionModal = ({
) { ) {
return Promise.resolve(); return Promise.resolve();
} else { } else {
return Promise.reject('Subscription表达式格式有误'); return Promise.reject(intl.get('Subscription表达式格式有误'));
} }
}, },
}, },

View File

@ -105,7 +105,7 @@ export default {
gotify: [ gotify: [
{ {
label: 'gotifyUrl', label: 'gotifyUrl',
tip: intl.get('gotify的url地址,例如 https://push.example.de:8080'), tip: intl.get('gotify的url地址例如 https://push.example.de:8080'),
required: true, required: true,
}, },
{ {
@ -126,16 +126,14 @@ export default {
goCqHttpBot: [ goCqHttpBot: [
{ {
label: 'goCqHttpBotUrl', label: 'goCqHttpBotUrl',
tip: intl.get( tip: intl.get('推送到个人QQ: http://127.0.0.1/send_private_msghttp://127.0.0.1/send_group_msg',
'推送到个人QQ: http://127.0.0.1/send_private_msghttp://127.0.0.1/send_group_msg',
), ),
required: true, required: true,
}, },
{ label: 'goCqHttpBotToken', tip: intl.get('访问密钥'), required: true }, { label: 'goCqHttpBotToken', tip: intl.get('访问密钥'), required: true },
{ {
label: 'goCqHttpBotQq', label: 'goCqHttpBotQq',
tip: intl.get( tip: intl.get('如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群',
'如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群',
), ),
required: true, required: true,
}, },
@ -155,26 +153,24 @@ export default {
}, },
{ {
label: 'pushDeerUrl', label: 'pushDeerUrl',
tip: intl.get( tip: intl.get('PushDeer的自架API endpoint默认是 https://api2.pushdeer.com/message/push',
'PushDeer的自架API endpoint默认是 https://api2.pushdeer.com/message/push',
), ),
}, },
], ],
bark: [ bark: [
{ {
label: 'barkPush', label: 'barkPush',
tip: intl.get( tip: intl.get('Bark的信息IP/设备码例如https://api.day.app/XXXXXXXX',
'Bark的信息IP/设备码例如https://api.day.app/XXXXXXXX',
), ),
required: true, required: true,
}, },
{ {
label: 'barkIcon', label: 'barkIcon',
tip: intl.get('BARK推送图标,自定义推送图标 (需iOS15或以上才能显示)'), tip: intl.get('BARK推送图标自定义推送图标 (需iOS15或以上才能显示)'),
}, },
{ {
label: 'barkSound', label: 'barkSound',
tip: intl.get('BARK推送铃声,铃声列表去APP查看复制填写'), tip: intl.get('BARK推送铃声铃声列表去APP查看复制填写'),
}, },
{ {
label: 'barkGroup', label: 'barkGroup',
@ -184,8 +180,7 @@ export default {
telegramBot: [ telegramBot: [
{ {
label: 'telegramBotToken', label: 'telegramBotToken',
tip: intl.get( tip: intl.get('telegram机器人的token例如1077xxx4424:AAFjv0FcqxxxxxxgEMGfi22B4yh15R5uw',
'telegram机器人的token例如1077xxx4424:AAFjv0FcqxxxxxxgEMGfi22B4yh15R5uw',
), ),
required: true, required: true,
}, },
@ -198,8 +193,7 @@ export default {
{ label: 'telegramBotProxyPort', tip: intl.get('代理端口') }, { label: 'telegramBotProxyPort', tip: intl.get('代理端口') },
{ {
label: 'telegramBotProxyAuth', label: 'telegramBotProxyAuth',
tip: intl.get( tip: intl.get('telegram代理配置认证参数用户名与密码用英文冒号连接 user:password',
'telegram代理配置认证参数, 用户名与密码用英文冒号连接 user:password',
), ),
}, },
{ {
@ -210,23 +204,20 @@ export default {
dingtalkBot: [ dingtalkBot: [
{ {
label: 'dingtalkBotToken', label: 'dingtalkBotToken',
tip: intl.get( tip: intl.get('钉钉机器人webhook token例如5a544165465465645d0f31dca676e7bd07415asdasd',
'钉钉机器人webhook token例如5a544165465465645d0f31dca676e7bd07415asdasd',
), ),
required: true, required: true,
}, },
{ {
label: 'dingtalkBotSecret', label: 'dingtalkBotSecret',
tip: intl.get( tip: intl.get('密钥机器人安全设置页面加签一栏下面显示的SEC开头的字符串',
'密钥机器人安全设置页面加签一栏下面显示的SEC开头的字符串',
), ),
}, },
], ],
weWorkBot: [ weWorkBot: [
{ {
label: 'weWorkBotKey', label: 'weWorkBotKey',
tip: intl.get( tip: intl.get('企业微信机器人的webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770)例如693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa',
'企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770)例如693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa',
), ),
required: true, required: true,
}, },
@ -238,8 +229,7 @@ export default {
weWorkApp: [ weWorkApp: [
{ {
label: 'weWorkAppKey', label: 'weWorkAppKey',
tip: intl.get( tip: intl.get('corpid、corpsecret、touser(注:多个成员ID使用|隔开)、agentid、消息类型(选填,不填默认文本消息类型) 注意用,号隔开(英文输入法的逗号)例如wwcfrs,B-76WERQ,qinglong,1000001,2COat',
'corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型) 注意用,号隔开(英文输入法的逗号)例如wwcfrs,B-76WERQ,qinglong,1000001,2COat',
), ),
required: true, required: true,
}, },
@ -251,8 +241,7 @@ export default {
aibotk: [ aibotk: [
{ {
label: 'aibotkKey', label: 'aibotkKey',
tip: intl.get( tip: intl.get('密钥key智能微秘书个人中心获取apikey申请地址https://wechat.aibotk.com/signup?from=ql',
'密钥key,智能微秘书个人中心获取apikey申请地址https://wechat.aibotk.com/signup?from=ql',
), ),
required: true, required: true,
}, },
@ -268,8 +257,7 @@ export default {
}, },
{ {
label: 'aibotkName', label: 'aibotkName',
tip: intl.get( tip: intl.get('要发送的用户昵称或群名,如果目标是群,需要填群名,如果目标是好友,需要填好友昵称',
'要发送的用户昵称或群名,如果目标是群,需要填群名,如果目标是好友,需要填好友昵称',
), ),
required: true, required: true,
}, },
@ -277,8 +265,7 @@ export default {
iGot: [ iGot: [
{ {
label: 'iGotPushKey', label: 'iGotPushKey',
tip: intl.get( tip: intl.get('iGot的信息推送key例如https://push.hellyw.com/XXXXXXXX',
'iGot的信息推送key例如https://push.hellyw.com/XXXXXXXX',
), ),
required: true, required: true,
}, },
@ -286,23 +273,20 @@ export default {
pushPlus: [ pushPlus: [
{ {
label: 'pushPlusToken', label: 'pushPlusToken',
tip: intl.get( tip: intl.get('微信扫码登录后一对一推送或一对多推送下面的token(您的Token)不提供PUSH_PLUS_USER则默认为一对一推送参考 https://www.pushplus.plus/',
'微信扫码登录后一对一推送或一对多推送下面的token(您的Token)不提供PUSH_PLUS_USER则默认为一对一推送参考 https://www.pushplus.plus/',
), ),
required: true, required: true,
}, },
{ {
label: 'pushPlusUser', label: 'pushPlusUser',
tip: intl.get( tip: intl.get('一对多推送的“群组编码”(一对多推送下面->您的群组(如无则创建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)',
'一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)',
), ),
}, },
], ],
lark: [ lark: [
{ {
label: 'larkKey', label: 'larkKey',
tip: intl.get( tip: intl.get('飞书群组机器人https://www.feishu.cn/hc/zh-CN/articles/360024984973',
'飞书群组机器人https://www.feishu.cn/hc/zh-CN/articles/360024984973',
), ),
required: true, required: true,
}, },
@ -310,8 +294,7 @@ export default {
email: [ email: [
{ {
label: 'emailService', label: 'emailService',
tip: intl.get( tip: intl.get('邮箱服务名称比如126、163、Gmail、QQ等支持列表https://nodemailer.com/smtp/well-known/',
'邮箱服务名称比如126、163、Gmail、QQ等支持列表https://nodemailer.com/smtp/well-known/',
), ),
required: true, required: true,
}, },
@ -344,8 +327,7 @@ export default {
}, },
{ {
label: 'webhookUrl', label: 'webhookUrl',
tip: intl.get( tip: intl.get('请求链接以http或者https开头。url或者body中必须包含$title$content可选对应api内容的位置',
'请求链接以http或者https开头。url或者body中必须包含$title$content可选对应api内容的位置',
), ),
required: true, required: true,
placeholder: 'https://xxx.cn/api?content=$title\n', placeholder: 'https://xxx.cn/api?content=$title\n',
@ -357,8 +339,7 @@ export default {
}, },
{ {
label: 'webhookBody', label: 'webhookBody',
tip: intl.get( tip: intl.get('请求体格式key1: value1多个换行分割。url或者body中必须包含$title$content可选对应api内容的位置',
'请求体格式key1: value1多个换行分割。url或者body中必须包含$title$content可选对应api内容的位置',
), ),
placeholder: 'key1: $title\nkey2: $content', placeholder: 'key1: $title\nkey2: $content',
}, },

View File

@ -1 +1,9 @@
export const LOG_END_SYMBOL = '     '; export const LOG_END_SYMBOL = '     ';
export const LANG_MAP = {
'.py': 'python',
'.js': 'javascript',
'.mjs': 'javascript',
'.sh': 'shell',
'.ts': 'typescript',
};

View File

@ -1,3 +1,4 @@
import intl from 'react-intl-universal'
import { message } from 'antd'; import { message } from 'antd';
import config from './config'; import config from './config';
import { history } from '@umijs/max'; import { history } from '@umijs/max';
@ -32,7 +33,7 @@ const errorHandler = function (error: AxiosError) {
history.push('/error'); history.push('/error');
} else if (responseStatus === 401) { } else if (responseStatus === 401) {
if (history.location.pathname !== '/login') { if (history.location.pathname !== '/login') {
message.error('登录已过期,请重新登录'); message.error(intl.get('登录已过期,请重新登录'));
localStorage.removeItem(config.authKey); localStorage.removeItem(config.authKey);
history.push('/login'); history.push('/login');
} }

View File

@ -1,5 +1,5 @@
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { LOG_END_SYMBOL } from './const'; import { LANG_MAP, LOG_END_SYMBOL } from './const';
import cron_parser from 'cron-parser'; import cron_parser from 'cron-parser';
export default function browserType() { export default function browserType() {
@ -154,9 +154,9 @@ export default function browserType() {
shell === 'none' shell === 'none'
? {} ? {}
: { : {
shell, // wechat qq uc 360 2345 sougou liebao maxthon shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs, shellVs,
}, },
); );
console.log( console.log(
@ -335,7 +335,18 @@ export function parseCrontab(schedule: string): Date {
if (time) { if (time) {
return time.next().toDate(); return time.next().toDate();
} }
} catch (error) {} } catch (error) { }
return new Date('1970'); return new Date('1970');
} }
export function getExtension(filename: string) {
if (!filename) return '';
const arr = filename.split('.');
return `.${arr[arr.length - 1]}`;
}
export function getEditorMode(filename: string) {
const extension = getExtension(filename) as keyof typeof LANG_MAP;
return LANG_MAP[extension];
}

View File

@ -1,6 +1,12 @@
version: 2.15.20 version: 2.16.0
changeLogLink: https://t.me/jiao_long/386 changeLogLink: https://t.me/jiao_long/388
publishTime: 2023-07-22 16:00 publishTime: 2023-08-06 16:00
changeLog: | changeLog: |
1. 修复判断 linux 依赖已安装逻辑 1. 多语言支持英文界面
2. 修复未启动完成时,页面提示 500 2. 定时任务增加关联订阅
3. 删除订阅支持自动删除任务和脚本
4. 定时任务支持运行 mjs 后缀文件
5. PushMe消息通道增加 params 参数
6. 修复 6 位 cron 不以 task 开头定时任务运行失败
7. 修复任务详情日志列表过多卡顿
8. 修复定时任务列表虚拟滚动