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',
fastRefresh: true,
favicons: [`https://qn.whyour.cn/favicon.svg`],
mfsu: {
strategy: 'eager',
},
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
proxy: {
[`${baseUrl}api/public`]: {

View File

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

View File

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

View File

@ -37,7 +37,7 @@ async function linkCommand() {
if (fs.existsSync(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`,
);
return callback(
new Error(`${scheduleErrLog || ''}\n${panelErrLog || ''}\n${res}`),
new Error(`${scheduleErrLog || ''}\n${panelErrLog || ''}\n${res}`.trim()),
);
default:

View File

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

View File

@ -458,7 +458,7 @@ class WeCom:
return respone["errmsg"]
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 = {
"touser": touser,
"msgtype": "mpnews",

View File

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

View File

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

View File

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

View File

@ -396,16 +396,57 @@
"关联订阅": "Associate Subscription",
"订阅": "Subscription",
"创建": "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",
"夹及其子文件": "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 })
.then(({ code, data }) => {
if (code === 200) {
message.success('保存成功');
message.success(intl.get('保存成功'));
}
setConfirmLoading(false);
});

View File

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

View File

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

View File

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

View File

@ -99,7 +99,7 @@ const CronModal = ({
if (!value || cronParse.parseExpression(value).hasNext()) {
return Promise.resolve();
} 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] })
.then(({ code, data }) => {
if (code === 200) {
message.success('删除成功');
message.success(intl.get('删除成功'));
cronViewChange();
}
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,21 +8,9 @@ import Editor from '@monaco-editor/react';
import SaveModal from './saveModal';
import SettingModal from './setting';
import { useTheme } from '@/utils/hooks';
import { logEnded } from '@/utils';
import { getEditorMode, logEnded } from '@/utils';
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 = ({
treeData,
@ -65,7 +53,7 @@ const EditModal = ({
return;
}
const newMode = LangMap[value.slice(-3)] || '';
const newMode = getEditorMode(value);
setCNode(node);
setLanguage(newMode);
getDetail(node);
@ -145,7 +133,7 @@ const EditModal = ({
setCNode(currentNode);
setValue(content as string);
setSelectedKey(currentNode.key);
const newMode = LangMap[currentNode.title.slice(-3)] || '';
const newMode = getEditorMode(currentNode.title);
setLanguage(newMode);
}
}, [content, currentNode]);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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';
export default function browserType() {
@ -154,9 +154,9 @@ export default function browserType() {
shell === 'none'
? {}
: {
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
);
console.log(
@ -335,7 +335,18 @@ export function parseCrontab(schedule: string): Date {
if (time) {
return time.next().toDate();
}
} catch (error) {}
} catch (error) { }
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
changeLogLink: https://t.me/jiao_long/386
publishTime: 2023-07-22 16:00
version: 2.16.0
changeLogLink: https://t.me/jiao_long/388
publishTime: 2023-08-06 16:00
changeLog: |
1. 修复判断 linux 依赖已安装逻辑
2. 修复未启动完成时,页面提示 500
1. 多语言支持英文界面
2. 定时任务增加关联订阅
3. 删除订阅支持自动删除任务和脚本
4. 定时任务支持运行 mjs 后缀文件
5. PushMe消息通道增加 params 参数
6. 修复 6 位 cron 不以 task 开头定时任务运行失败
7. 修复任务详情日志列表过多卡顿
8. 修复定时任务列表虚拟滚动