系统设置增加依赖代理和镜像源设置

This commit is contained in:
whyour 2023-11-06 23:55:16 +08:00
parent 4a3ac7dc4b
commit 9ed107980c
10 changed files with 213 additions and 38 deletions

View File

@ -5,7 +5,7 @@ import { NotificationInfo } from './notify';
export class AuthInfo { export class AuthInfo {
ip?: string; ip?: string;
type: AuthDataType; type: AuthDataType;
info?: AuthModelInfo; info?: SystemModelInfo;
id?: number; id?: number;
constructor(options: AuthInfo) { constructor(options: AuthInfo) {
@ -32,6 +32,10 @@ export enum AuthDataType {
export interface SystemConfigInfo { export interface SystemConfigInfo {
logRemoveFrequency?: number; logRemoveFrequency?: number;
cronConcurrency?: number; cronConcurrency?: number;
dependenceProxy?: string;
nodeMirror?: string;
pythonMirror?: string;
linuxMirror?: string;
} }
export interface LoginLogInfo { export interface LoginLogInfo {
@ -42,12 +46,12 @@ export interface LoginLogInfo {
status?: LoginStatus; status?: LoginStatus;
} }
export type AuthModelInfo = SystemConfigInfo & export type SystemModelInfo = SystemConfigInfo &
Partial<NotificationInfo> & Partial<NotificationInfo> &
LoginLogInfo; LoginLogInfo;
export interface AuthInstance extends Model<AuthInfo, AuthInfo>, AuthInfo {} export interface SystemInstance extends Model<AuthInfo, AuthInfo>, AuthInfo { }
export const AuthModel = sequelize.define<AuthInstance>('Auth', { export const SystemModel = sequelize.define<SystemInstance>('Auth', {
ip: DataTypes.STRING, ip: DataTypes.STRING,
type: DataTypes.STRING, type: DataTypes.STRING,
info: { info: {

View File

@ -5,7 +5,7 @@ import { EnvModel } from '../data/env';
import { CrontabModel } from '../data/cron'; import { CrontabModel } from '../data/cron';
import { DependenceModel } from '../data/dependence'; import { DependenceModel } from '../data/dependence';
import { AppModel } from '../data/open'; import { AppModel } from '../data/open';
import { AuthModel } from '../data/auth'; import { SystemModel } from '../data/system';
import { fileExist } from '../config/util'; import { fileExist } from '../config/util';
import { SubscriptionModel } from '../data/subscription'; import { SubscriptionModel } from '../data/subscription';
import { CrontabViewModel } from '../data/cronView'; import { CrontabViewModel } from '../data/cronView';
@ -17,7 +17,7 @@ export default async () => {
await CrontabModel.sync(); await CrontabModel.sync();
await DependenceModel.sync(); await DependenceModel.sync();
await AppModel.sync(); await AppModel.sync();
await AuthModel.sync(); await SystemModel.sync();
await EnvModel.sync(); await EnvModel.sync();
await SubscriptionModel.sync(); await SubscriptionModel.sync();
await CrontabViewModel.sync(); await CrontabViewModel.sync();
@ -75,7 +75,7 @@ export default async () => {
const dependenceCount = await DependenceModel.count(); const dependenceCount = await DependenceModel.count();
const envCount = await EnvModel.count(); const envCount = await EnvModel.count();
const appCount = await AppModel.count(); const appCount = await AppModel.count();
const authCount = await AuthModel.count(); const authCount = await SystemModel.count();
if (crondbExist && cronCount === 0) { if (crondbExist && cronCount === 0) {
const cronDb = new DataStore({ const cronDb = new DataStore({
filename: cronDbFile, filename: cronDbFile,
@ -127,7 +127,7 @@ export default async () => {
}); });
authDb.persistence.compactDatafile(); authDb.persistence.compactDatafile();
authDb.find({}).exec(async (err, docs) => { authDb.find({}).exec(async (err, docs) => {
await AuthModel.bulkCreate(docs, { ignoreDuplicates: true }); await SystemModel.bulkCreate(docs, { ignoreDuplicates: true });
}); });
} }

View File

@ -5,10 +5,10 @@ import config from '../config';
import { import {
AuthDataType, AuthDataType,
AuthInfo, AuthInfo,
AuthInstance, SystemInstance,
AuthModel, SystemModel,
AuthModelInfo, SystemModelInfo,
} from '../data/auth'; } from '../data/system';
import { NotificationInfo } from '../data/notify'; import { NotificationInfo } from '../data/notify';
import NotificationService from './notify'; import NotificationService from './notify';
import ScheduleService, { TaskCallbacks } from './schedule'; import ScheduleService, { TaskCallbacks } from './schedule';
@ -43,17 +43,17 @@ export default class SystemService {
public async getSystemConfig() { public async getSystemConfig() {
const doc = await this.getDb({ type: AuthDataType.systemConfig }); const doc = await this.getDb({ type: AuthDataType.systemConfig });
return doc || ({} as AuthInstance); return doc || ({} as SystemInstance);
} }
private async updateAuthDb(payload: AuthInfo): Promise<AuthInstance> { private async updateAuthDb(payload: AuthInfo): Promise<SystemInstance> {
await AuthModel.upsert({ ...payload }); await SystemModel.upsert({ ...payload });
const doc = await this.getDb({ type: payload.type }); const doc = await this.getDb({ type: payload.type });
return doc; return doc;
} }
public async getDb(query: any): Promise<AuthInstance> { public async getDb(query: any): Promise<SystemInstance> {
const doc: any = await AuthModel.findOne({ where: { ...query } }); const doc: any = await SystemModel.findOne({ where: { ...query } });
return doc && doc.get({ plain: true }); return doc && doc.get({ plain: true });
} }
@ -75,11 +75,10 @@ export default class SystemService {
} }
} }
public async updateSystemConfig(info: AuthModelInfo) { public async updateSystemConfig(info: SystemModelInfo) {
const oDoc = await this.getSystemConfig(); const oDoc = await this.getSystemConfig();
const result = await this.updateAuthDb({ const result = await this.updateAuthDb({
...oDoc, ...oDoc,
type: AuthDataType.systemConfig,
info, info,
}); });
if (info.logRemoveFrequency) { if (info.logRemoveFrequency) {

View File

@ -14,10 +14,10 @@ import { authenticator } from '@otplib/preset-default';
import { import {
AuthDataType, AuthDataType,
AuthInfo, AuthInfo,
AuthModel, SystemModel,
AuthModelInfo, SystemModelInfo,
LoginStatus, LoginStatus,
} from '../data/auth'; } from '../data/system';
import { NotificationInfo } from '../data/notify'; import { NotificationInfo } from '../data/notify';
import NotificationService from './notify'; import NotificationService from './notify';
import { Request } from 'express'; import { Request } from 'express';
@ -195,8 +195,8 @@ export default class UserService {
}); });
} }
public async getLoginLog(): Promise<Array<AuthModelInfo | undefined>> { public async getLoginLog(): Promise<Array<SystemModelInfo | undefined>> {
const docs = await AuthModel.findAll({ const docs = await SystemModel.findAll({
where: { type: AuthDataType.loginLog }, where: { type: AuthDataType.loginLog },
}); });
if (docs && docs.length > 0) { if (docs && docs.length > 0) {
@ -204,7 +204,7 @@ export default class UserService {
(a, b) => b.info!.timestamp! - a.info!.timestamp!, (a, b) => b.info!.timestamp! - a.info!.timestamp!,
); );
if (result.length > 100) { if (result.length > 100) {
await AuthModel.destroy({ await SystemModel.destroy({
where: { id: result[result.length - 1].id }, where: { id: result[result.length - 1].id },
}); });
} }
@ -214,7 +214,7 @@ export default class UserService {
} }
private async insertDb(payload: AuthInfo): Promise<AuthInfo> { private async insertDb(payload: AuthInfo): Promise<AuthInfo> {
const doc = await AuthModel.create({ ...payload }, { returning: true }); const doc = await SystemModel.create({ ...payload }, { returning: true });
return doc; return doc;
} }
@ -345,21 +345,21 @@ export default class UserService {
} }
private async updateAuthDb(payload: AuthInfo): Promise<any> { private async updateAuthDb(payload: AuthInfo): Promise<any> {
let doc = await AuthModel.findOne({ type: payload.type }); let doc = await SystemModel.findOne({ type: payload.type });
if (doc) { if (doc) {
const updateResult = await AuthModel.update(payload, { const updateResult = await SystemModel.update(payload, {
where: { id: doc.id }, where: { id: doc.id },
returning: true, returning: true,
}); });
doc = updateResult[1][0]; doc = updateResult[1][0];
} else { } else {
doc = await AuthModel.create(payload, { returning: true }); doc = await SystemModel.create(payload, { returning: true });
} }
return doc; return doc;
} }
public async getDb(query: any): Promise<any> { public async getDb(query: any): Promise<any> {
const doc: any = await AuthModel.findOne({ where: { ...query } }); const doc: any = await SystemModel.findOne({ where: { ...query } });
return doc && (doc.get({ plain: true }) as any); return doc && (doc.get({ plain: true }) as any);
} }

View File

@ -1,6 +1,6 @@
import PQueue, { QueueAddOptions } from 'p-queue-cjs'; import PQueue, { QueueAddOptions } from 'p-queue-cjs';
import os from 'os'; import os from 'os';
import { AuthDataType, AuthModel } from '../data/auth'; import { AuthDataType, SystemModel } from '../data/system';
import Logger from '../loaders/logger'; import Logger from '../loaders/logger';
class TaskLimit { class TaskLimit {
@ -59,8 +59,8 @@ class TaskLimit {
this.cronLimit.concurrency = limit; this.cronLimit.concurrency = limit;
return; return;
} }
await AuthModel.sync(); await SystemModel.sync();
const doc = await AuthModel.findOne({ const doc = await SystemModel.findOne({
where: { type: AuthDataType.systemConfig }, where: { type: AuthDataType.systemConfig },
}); });
if (doc?.info?.cronConcurrency) { if (doc?.info?.cronConcurrency) {

View File

@ -10,6 +10,9 @@ DefaultCronRule=""
## ql repo命令拉取脚本时需要拉取的文件后缀直接写文件后缀名即可 ## ql repo命令拉取脚本时需要拉取的文件后缀直接写文件后缀名即可
RepoFileExtensions="js py" RepoFileExtensions="js py"
## 代理地址支持HTTP/SOCK5例如 http://127.0.0.1:7890
ProxyUrl=""
## 资源告警阙值默认CPU 80%、内存80%、磁盘90% ## 资源告警阙值默认CPU 80%、内存80%、磁盘90%
CpuWarn=80 CpuWarn=80
MemoryWarn=80 MemoryWarn=80
@ -18,7 +21,7 @@ DiskWarn=90
## 设置定时任务执行的超时时间例如1h后缀"s"代表秒(默认值), "m"代表分, "h"代表小时, "d"代表天 ## 设置定时任务执行的超时时间例如1h后缀"s"代表秒(默认值), "m"代表分, "h"代表小时, "d"代表天
CommandTimeoutTime="" CommandTimeoutTime=""
## 在使用 task 命令执行 JavaScript 脚本1时,随机延迟启动任务的最大延迟时间 ## 在运行 task 命令时,随机延迟启动任务的最大延迟时间
## 默认给javascript任务加随机延迟如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行,取消延迟赋值为空 ## 默认给javascript任务加随机延迟如 RandomDelay="300" ,表示任务将在 1-300 秒内随机延迟一个秒数,然后再运行,取消延迟赋值为空
RandomDelay="" RandomDelay=""
@ -30,8 +33,8 @@ RandomDelayFileExtensions=""
## 默认是第0分钟和第30分钟例如21:00或21:30分的任务将会准点运行。不需要准点运行赋值为空 ## 默认是第0分钟和第30分钟例如21:00或21:30分的任务将会准点运行。不需要准点运行赋值为空
RandomDelayIgnoredMinutes="" RandomDelayIgnoredMinutes=""
## 如果你自己会写shell脚本并且希望在每次容器启动时,额外运行你的 shell 脚本,请赋值为 "true"默认为true ## 如果你自己会写shell脚本并且希望在每次运行 ql update 命令时,额外运行你的 shell 脚本,请赋值为 "true"默认为true
EnableExtraShell="" EnableExtraShell="true"
## 是否自动启动bot默认不启动设置为true时自动启动目前需要自行克隆bot仓库所需代码存到ql/repo目录下文件夹命名为dockerbot ## 是否自动启动bot默认不启动设置为true时自动启动目前需要自行克隆bot仓库所需代码存到ql/repo目录下文件夹命名为dockerbot
AutoStartBot="" AutoStartBot=""
@ -39,6 +42,12 @@ AutoStartBot=""
## 是否使用第三方bot默认不使用使用时填入仓库地址存到ql/repo目录下文件夹命名为diybot ## 是否使用第三方bot默认不使用使用时填入仓库地址存到ql/repo目录下文件夹命名为diybot
BotRepoUrl="" BotRepoUrl=""
## 安装python依赖时指定pip源
PipMirror=""
## 安装node依赖时指定npm源
NpmMirror=""
## 通知环境变量 ## 通知环境变量
## 1. Server酱 ## 1. Server酱
## https://sct.ftqq.com ## https://sct.ftqq.com

View File

@ -466,5 +466,14 @@
"个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx": "Individuals: user_id=individual QQ Groups fill in group_id=QQ Groups more than one with English; separated by the same time to support individuals and groups such as: user_id=xxx;group_id=xxxx;group_id=xxxxx", "个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx": "Individuals: user_id=individual QQ Groups fill in group_id=QQ Groups more than one with English; separated by the same time to support individuals and groups such as: user_id=xxx;group_id=xxxx;group_id=xxxxx",
"docker安装在持久化config目录下的chronocat.yml文件可找到": "The docker installation can be found in the persistence config directory in the chronocat.yml file", "docker安装在持久化config目录下的chronocat.yml文件可找到": "The docker installation can be found in the persistence config directory in the chronocat.yml file",
"请选择": "Please select", "请选择": "Please select",
"请输入": "Please input" "请输入": "Please input",
"依赖设置": "Dependence Settings",
"Node 软件包镜像源": "Node Software Package Mirror Source",
"Python 软件包镜像源": "Python Software Package Mirror Source",
"Linux 软件包镜像源": "Linux Software Package Mirror Source",
"代理与镜像源二选一即可": "Either Proxy or Mirror Source can be chosen",
"代理地址, 支持HTTP(S)/SOCK5": "Proxy Address, supports HTTP(S)/SOCK5",
"NPM 镜像源": "NPM Mirror Source",
"PyPI 镜像源": "PyPI Mirror Source",
"alpine linux 镜像源": "Alpine Linux Mirror Source"
} }

View File

@ -466,5 +466,14 @@
"个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx": "个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx", "个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx": "个人:user_id=个人QQ 群则填入group_id=QQ群 多个用英文;隔开同时支持个人和群 如user_id=xxx;group_id=xxxx;group_id=xxxxx",
"docker安装在持久化config目录下的chronocat.yml文件可找到": "docker安装在持久化config目录下的chronocat.yml文件可找到", "docker安装在持久化config目录下的chronocat.yml文件可找到": "docker安装在持久化config目录下的chronocat.yml文件可找到",
"请选择": "请选择", "请选择": "请选择",
"请输入": "请输入" "请输入": "请输入",
"依赖设置": "依赖设置",
"Node 软件包镜像源": "Node 软件包镜像源",
"Python 软件包镜像源": "Python 软件包镜像源",
"Linux 软件包镜像源": "Linux 软件包镜像源",
"代理与镜像源二选一即可": "代理与镜像源二选一即可",
"代理地址, 支持HTTP(S)/SOCK5": "代理地址, 支持HTTP(S)/SOCK5",
"NPM 镜像源": "NPM 镜像源",
"PyPI 镜像源": "PyPI 镜像源",
"alpine linux 镜像源": "alpine linux 镜像源"
} }

View File

@ -0,0 +1,139 @@
import intl from 'react-intl-universal';
import React, { useState, useEffect, useRef } from 'react';
import { Button, InputNumber, Form, message, Input, Alert } from 'antd';
import config from '@/utils/config';
import { request } from '@/utils/http';
import './index.less';
const Dependence = () => {
const [systemConfig, setSystemConfig] = useState<{
dependenceProxy?: string;
nodeMirror?: string;
pythonMirror?: string;
linuxMirror?: string;
}>();
const [form] = Form.useForm();
const getSystemConfig = () => {
request
.get(`${config.apiPrefix}system/config`)
.then(({ code, data }) => {
if (code === 200 && data.info) {
setSystemConfig(data.info);
}
})
.catch((error: any) => {
console.log(error);
});
};
const updateSystemConfig = () => {
request
.put(`${config.apiPrefix}system/config`, systemConfig)
.then(({ code, data }) => {
if (code === 200) {
message.success(intl.get('更新成功'));
}
})
.catch((error: any) => {
console.log(error);
});
};
useEffect(() => {
getSystemConfig();
}, []);
return (
<Form layout="vertical" form={form}>
<Form.Item
label={intl.get('代理')}
name="proxy"
extra={intl.get('代理与镜像源二选一即可')}
>
<Input.Group compact>
<Input
placeholder={intl.get('代理地址, 支持HTTP(S)/SOCK5')}
style={{ width: 330 }}
value={systemConfig?.dependenceProxy}
onChange={(e) => {
setSystemConfig({
...systemConfig,
dependenceProxy: e.target.value,
});
}}
/>
<Button
type="primary"
onClick={updateSystemConfig}
style={{ width: 84 }}
>
{intl.get('确认')}
</Button>
</Input.Group>
</Form.Item>
<Form.Item label={intl.get('Node 软件包镜像源')} name="node">
<Input.Group compact>
<Input
style={{ width: 330 }}
placeholder={intl.get('NPM 镜像源')}
value={systemConfig?.nodeMirror}
onChange={(e) => {
setSystemConfig({ ...systemConfig, nodeMirror: e.target.value });
}}
/>
<Button
type="primary"
onClick={updateSystemConfig}
style={{ width: 84 }}
>
{intl.get('确认')}
</Button>
</Input.Group>
</Form.Item>
<Form.Item label={intl.get('Python 软件包镜像源')} name="python">
<Input.Group compact>
<Input
style={{ width: 330 }}
placeholder={intl.get('PyPI 镜像源')}
value={systemConfig?.pythonMirror}
onChange={(e) => {
setSystemConfig({
...systemConfig,
pythonMirror: e.target.value,
});
}}
/>
<Button
type="primary"
onClick={updateSystemConfig}
style={{ width: 84 }}
>
{intl.get('确认')}
</Button>
</Input.Group>
</Form.Item>
<Form.Item label={intl.get('Linux 软件包镜像源')} name="linux">
<Input.Group compact>
<Input
style={{ width: 330 }}
placeholder={intl.get('alpine linux 镜像源')}
value={systemConfig?.linuxMirror}
onChange={(e) => {
setSystemConfig({ ...systemConfig, linuxMirror: e.target.value });
}}
/>
<Button
type="primary"
onClick={updateSystemConfig}
style={{ width: 84 }}
>
{intl.get('确认')}
</Button>
</Input.Group>
</Form.Item>
</Form>
);
};
export default Dependence;

View File

@ -34,6 +34,7 @@ import { SharedContext } from '@/layouts';
import './index.less'; import './index.less';
import useResizeObserver from '@react-hook/resize-observer'; import useResizeObserver from '@react-hook/resize-observer';
import SystemLog from './systemLog'; import SystemLog from './systemLog';
import Dependence from './dependence';
const { Text } = Typography; const { Text } = Typography;
const isDemoEnv = window.__ENV__DeployEnv === 'demo'; const isDemoEnv = window.__ENV__DeployEnv === 'demo';
@ -354,6 +355,11 @@ const Setting = () => {
label: intl.get('登录日志'), label: intl.get('登录日志'),
children: <LoginLog data={loginLogData} />, children: <LoginLog data={loginLogData} />,
}, },
{
key: 'dependence',
label: intl.get('依赖设置'),
children: <Dependence />
},
{ {
key: 'other', key: 'other',
label: intl.get('其他设置'), label: intl.get('其他设置'),