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

This commit is contained in:
whyour 2023-11-25 21:49:33 +08:00
parent 9b7d4cb8fe
commit 3777a4e7b4
13 changed files with 455 additions and 156 deletions

View File

@ -6,7 +6,6 @@ import config from '../config';
import SystemService from '../services/system'; import SystemService from '../services/system';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
import UserService from '../services/user'; import UserService from '../services/user';
import { EnvModel } from '../data/env';
import { import {
getUniqPath, getUniqPath,
handleLogPath, handleLogPath,
@ -78,18 +77,16 @@ export default (app: Router) => {
); );
route.put( route.put(
'/config', '/config/log-remove-frequency',
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
logRemoveFrequency: Joi.number().optional().allow(null), logRemoveFrequency: Joi.number().allow(null),
cronConcurrency: Joi.number().optional().allow(null),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
const logger: Logger = Container.get('logger');
try { try {
const systemService = Container.get(SystemService); const systemService = Container.get(SystemService);
const result = await systemService.updateSystemConfig(req.body); const result = await systemService.updateLogRemoveFrequency(req.body);
res.send(result); res.send(result);
} catch (e) { } catch (e) {
return next(e); return next(e);
@ -97,6 +94,96 @@ export default (app: Router) => {
}, },
); );
route.put(
'/config/cron-concurrency',
celebrate({
body: Joi.object({
cronConcurrency: Joi.number().allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.updateCronConcurrency(req.body);
res.send(result);
} catch (e) {
return next(e);
}
},
);
route.put(
'/config/dependence-proxy',
celebrate({
body: Joi.object({
dependenceProxy: Joi.string().allow('').allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.updateDependenceProxy(req.body);
res.send(result);
} catch (e) {
return next(e);
}
},
);
route.put(
'/config/node-mirror',
celebrate({
body: Joi.object({
nodeMirror: Joi.string().allow('').allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
res.setHeader('Content-type', 'application/octet-stream');
await systemService.updateNodeMirror(req.body, res);
} catch (e) {
return next(e);
}
},
);
route.put(
'/config/python-mirror',
celebrate({
body: Joi.object({
pythonMirror: Joi.string().allow('').allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
const result = await systemService.updatePythonMirror(req.body);
res.send(result);
} catch (e) {
return next(e);
}
},
);
route.put(
'/config/linux-mirror',
celebrate({
body: Joi.object({
linuxMirror: Joi.string().allow('').allow(null),
}),
}),
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
res.setHeader('Content-type', 'application/octet-stream');
await systemService.updateLinuxMirror(req.body, res);
} catch (e) {
return next(e);
}
},
);
route.put( route.put(
'/update-check', '/update-check',
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {

View File

@ -47,6 +47,7 @@ const configString = 'config sample crontab shareCode diy';
const versionFile = path.join(rootPath, 'version.yaml'); const versionFile = path.join(rootPath, 'version.yaml');
const dataTgzFile = path.join(tmpPath, 'data.tgz'); const dataTgzFile = path.join(tmpPath, 'data.tgz');
const shareShellFile = path.join(shellPath, 'share.sh'); const shareShellFile = path.join(shellPath, 'share.sh');
const dependenceProxyFile = path.join(configPath, 'dependence-proxy.sh');
if (envFound.error) { if (envFound.error) {
throw new Error("⚠️ Couldn't find .env file ⚠️"); throw new Error("⚠️ Couldn't find .env file ⚠️");
@ -68,6 +69,7 @@ export default {
dataPath, dataPath,
dataTgzFile, dataTgzFile,
shareShellFile, shareShellFile,
dependenceProxyFile,
configString, configString,
loginFaild, loginFaild,
authError, authError,
@ -89,6 +91,7 @@ export default {
'config.sh.sample', 'config.sh.sample',
'cookie.sh', 'cookie.sh',
'crontab.list', 'crontab.list',
'dependence-proxy.sh',
'env.sh', 'env.sh',
'token.json', 'token.json',
], ],

View File

@ -496,4 +496,4 @@ export async function rmPath(path: string) {
} catch (error) { } catch (error) {
Logger.error('[rmPath失败]', error) Logger.error('[rmPath失败]', error)
} }
} }

View File

@ -14,7 +14,7 @@ import {
import { spawn } from 'cross-spawn'; import { spawn } from 'cross-spawn';
import SockService from './sock'; import SockService from './sock';
import { FindOptions, Op } from 'sequelize'; import { FindOptions, Op } from 'sequelize';
import { promiseExecSuccess } from '../config/util'; import { fileExist, promiseExecSuccess } from '../config/util';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import taskLimit from '../shared/pLimit'; import taskLimit from '../shared/pLimit';
@ -252,10 +252,18 @@ export default class DependenceService {
return resolve(null); return resolve(null);
} }
} }
const dependenceProxyFileExist = await fileExist(
const cp = spawn(`${depRunCommand} ${dependency.name.trim()}`, { config.dependenceProxyFile,
shell: '/bin/bash', );
}); const proxyStr = dependenceProxyFileExist
? `pnpm config get registry && source ${config.dependenceProxyFile} &&`
: '';
const cp = spawn(
`${proxyStr} ${depRunCommand} ${dependency.name.trim()}`,
{
shell: '/bin/bash',
},
);
cp.stdout.on('data', async (data) => { cp.stdout.on('data', async (data) => {
this.sockService.sendMessage({ this.sockService.sendMessage({

View File

@ -75,31 +75,140 @@ export default class SystemService {
} }
} }
public async updateSystemConfig(info: SystemModelInfo) { public async updateLogRemoveFrequency(info: SystemModelInfo) {
const oDoc = await this.getSystemConfig(); const oDoc = await this.getSystemConfig();
const result = await this.updateAuthDb({ const result = await this.updateAuthDb({
...oDoc, ...oDoc,
info, info: { ...oDoc.info, ...info },
}); });
if (info.logRemoveFrequency) { const cron = {
const cron = { id: result.id || NaN,
id: result.id || NaN, name: '删除日志',
name: '删除日志', command: `ql rmlog ${info.logRemoveFrequency}`,
command: `ql rmlog ${info.logRemoveFrequency}`, };
}; if (oDoc.info?.logRemoveFrequency) {
await this.scheduleService.cancelIntervalTask(cron); await this.scheduleService.cancelIntervalTask(cron);
if (info.logRemoveFrequency > 0) {
this.scheduleService.createIntervalTask(cron, {
days: info.logRemoveFrequency,
});
}
} }
if (info.logRemoveFrequency && info.logRemoveFrequency > 0) {
this.scheduleService.createIntervalTask(cron, {
days: info.logRemoveFrequency,
});
}
return { code: 200, data: info };
}
public async updateCronConcurrency(info: SystemModelInfo) {
const oDoc = await this.getSystemConfig();
await this.updateAuthDb({
...oDoc,
info: { ...oDoc.info, ...info },
});
if (info.cronConcurrency) { if (info.cronConcurrency) {
await taskLimit.setCustomLimit(info.cronConcurrency); await taskLimit.setCustomLimit(info.cronConcurrency);
} }
return { code: 200, data: info }; return { code: 200, data: info };
} }
public async updateDependenceProxy(info: SystemModelInfo) {
const oDoc = await this.getSystemConfig();
await this.updateAuthDb({
...oDoc,
info: { ...oDoc.info, ...info },
});
if (info.dependenceProxy) {
await fs.promises.writeFile(
config.dependenceProxyFile,
`export http_proxy="${info.dependenceProxy}"\nexport https_proxy="${info.dependenceProxy}"`,
);
} else {
await fs.promises.rm(config.dependenceProxyFile);
}
return { code: 200, data: info };
}
public async updateNodeMirror(info: SystemModelInfo, res: Response) {
const oDoc = await this.getSystemConfig();
await this.updateAuthDb({
...oDoc,
info: { ...oDoc.info, ...info },
});
let cmd = 'pnpm config delete registry';
if (info.nodeMirror) {
cmd = `pnpm config set registry ${info.nodeMirror}`;
}
const command = `cd && ${cmd} && pnpm i -g`;
this.scheduleService.runTask(
command,
{
onStart: async (cp) => {
res.setHeader('QL-Task-Pid', `${cp.pid}`);
},
onEnd: async () => {
res.end();
},
onError: async (message: string) => {
res.write(`\n${message}`);
},
onLog: async (message: string) => {
res.write(`\n${message}`);
},
},
{
command,
},
);
}
public async updatePythonMirror(info: SystemModelInfo) {
const oDoc = await this.getSystemConfig();
await this.updateAuthDb({
...oDoc,
info: { ...oDoc.info, ...info },
});
let cmd = 'pip config unset global.index-url';
if (info.pythonMirror) {
cmd = `pip3 config set global.index-url ${info.pythonMirror}`;
}
await promiseExec(cmd);
return { code: 200, data: info };
}
public async updateLinuxMirror(info: SystemModelInfo, res: Response) {
const oDoc = await this.getSystemConfig();
await this.updateAuthDb({
...oDoc,
info: { ...oDoc.info, ...info },
});
let targetDomain = 'dl-cdn.alpinelinux.org';
if (info.linuxMirror) {
targetDomain = info.linuxMirror;
}
const command = `sed -i 's/${
oDoc.info?.linuxMirror || 'dl-cdn.alpinelinux.org'
}/${targetDomain}/g' /etc/apk/repositories && apk update -f`;
this.scheduleService.runTask(
command,
{
onStart: async (cp) => {
res.setHeader('QL-Task-Pid', `${cp.pid}`);
},
onEnd: async () => {
res.end();
},
onError: async (message: string) => {
res.write(`\n${message}`);
},
onLog: async (message: string) => {
res.write(`\n${message}`);
},
},
{
command,
},
);
}
public async checkUpdate() { public async checkUpdate() {
try { try {
const currentVersionContent = await parseVersion(config.versionFile); const currentVersionContent = await parseVersion(config.versionFile);

View File

@ -86,7 +86,6 @@
"p-queue-cjs": "7.3.4", "p-queue-cjs": "7.3.4",
"protobufjs": "^7.2.3", "protobufjs": "^7.2.3",
"pstree.remy": "^1.1.8", "pstree.remy": "^1.1.8",
"react-hotkeys-hook": "^4.4.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"sequelize": "^6.25.5", "sequelize": "^6.25.5",
"serve-handler": "^6.1.3", "serve-handler": "^6.1.3",
@ -140,10 +139,11 @@
"axios": "^1.4.0", "axios": "^1.4.0",
"compression-webpack-plugin": "9.2.0", "compression-webpack-plugin": "9.2.0",
"concurrently": "^7.0.0", "concurrently": "^7.0.0",
"react-hotkeys-hook": "^4.4.1",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"monaco-editor": "0.33.0", "monaco-editor": "0.33.0",
"nodemon": "^2.0.15", "nodemon": "^3.0.1",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"qiniu": "^7.4.0", "qiniu": "^7.4.0",
"qrcode.react": "^1.0.1", "qrcode.react": "^1.0.1",

View File

@ -1,9 +1,5 @@
lockfileVersion: '6.0' lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies: dependencies:
'@grpc/grpc-js': '@grpc/grpc-js':
specifier: ^1.8.13 specifier: ^1.8.13
@ -98,9 +94,6 @@ dependencies:
pstree.remy: pstree.remy:
specifier: ^1.1.8 specifier: ^1.1.8
version: 1.1.8 version: 1.1.8
react-hotkeys-hook:
specifier: ^4.4.1
version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
reflect-metadata: reflect-metadata:
specifier: ^0.1.13 specifier: ^0.1.13
version: 0.1.13 version: 0.1.13
@ -266,8 +259,8 @@ devDependencies:
specifier: 0.33.0 specifier: 0.33.0
version: 0.33.0 version: 0.33.0
nodemon: nodemon:
specifier: ^2.0.15 specifier: ^3.0.1
version: 2.0.22 version: 3.0.1
prettier: prettier:
specifier: ^2.5.1 specifier: ^2.5.1
version: 2.8.8 version: 2.8.8
@ -304,6 +297,9 @@ devDependencies:
react-dom: react-dom:
specifier: 18.2.0 specifier: 18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-hotkeys-hook:
specifier: ^4.4.1
version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
react-intl-universal: react-intl-universal:
specifier: ^2.6.21 specifier: ^2.6.21
version: 2.6.21(react@18.2.0) version: 2.6.21(react@18.2.0)
@ -4139,7 +4135,7 @@ packages:
requiresBuild: true requiresBuild: true
dependencies: dependencies:
'@gar/promisify': 1.1.3 '@gar/promisify': 1.1.3
semver: 7.5.1 semver: 7.5.4
dev: false dev: false
optional: true optional: true
@ -5182,7 +5178,7 @@ packages:
ignore: 5.2.4 ignore: 5.2.4
natural-compare-lite: 1.4.0 natural-compare-lite: 1.4.0
regexpp: 3.2.0 regexpp: 3.2.0
semver: 7.5.1 semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2) tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
@ -5273,7 +5269,7 @@ packages:
debug: 4.3.4 debug: 4.3.4
globby: 11.1.0 globby: 11.1.0
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.5.1 semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2) tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
@ -5294,7 +5290,7 @@ packages:
debug: 4.3.4 debug: 4.3.4
globby: 11.1.0 globby: 11.1.0
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.5.1 semver: 7.5.4
tsutils: 3.21.0(typescript@5.2.2) tsutils: 3.21.0(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
@ -5318,7 +5314,7 @@ packages:
eslint: 8.35.0 eslint: 8.35.0
eslint-scope: 5.1.1 eslint-scope: 5.1.1
eslint-utils: 3.0.0(eslint@8.35.0) eslint-utils: 3.0.0(eslint@8.35.0)
semver: 7.5.1 semver: 7.5.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
@ -5341,7 +5337,7 @@ packages:
'@typescript-eslint/typescript-estree': 5.59.9(typescript@5.2.2) '@typescript-eslint/typescript-estree': 5.59.9(typescript@5.2.2)
eslint: 8.35.0 eslint: 8.35.0
eslint-scope: 5.1.1 eslint-scope: 5.1.1
semver: 7.5.1 semver: 7.5.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
@ -7717,7 +7713,7 @@ packages:
postcss-modules-scope: 3.0.0(postcss@8.4.24) postcss-modules-scope: 3.0.0(postcss@8.4.24)
postcss-modules-values: 4.0.0(postcss@8.4.24) postcss-modules-values: 4.0.0(postcss@8.4.24)
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
semver: 7.5.1 semver: 7.5.4
webpack: 5.85.1 webpack: 5.85.1
dev: true dev: true
@ -9053,7 +9049,7 @@ packages:
minimatch: 3.1.2 minimatch: 3.1.2
node-abort-controller: 3.1.1 node-abort-controller: 3.1.1
schema-utils: 3.1.2 schema-utils: 3.1.2
semver: 7.5.1 semver: 7.5.4
tapable: 2.2.1 tapable: 2.2.1
typescript: 5.2.2 typescript: 5.2.2
webpack: 5.85.1 webpack: 5.85.1
@ -10291,6 +10287,7 @@ packages:
/js-tokens@4.0.0: /js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
/js-yaml@3.14.1: /js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
@ -10745,6 +10742,7 @@ packages:
hasBin: true hasBin: true
dependencies: dependencies:
js-tokens: 4.0.0 js-tokens: 4.0.0
dev: true
/lower-case@2.0.2: /lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
@ -11323,9 +11321,9 @@ packages:
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dev: false dev: false
/nodemon@2.0.22: /nodemon@3.0.1:
resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} resolution: {integrity: sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==}
engines: {node: '>=8.10.0'} engines: {node: '>=10'}
hasBin: true hasBin: true
dependencies: dependencies:
chokidar: 3.5.3 chokidar: 3.5.3
@ -11333,8 +11331,8 @@ packages:
ignore-by-default: 1.0.1 ignore-by-default: 1.0.1
minimatch: 3.1.2 minimatch: 3.1.2
pstree.remy: 1.1.8 pstree.remy: 1.1.8
semver: 5.7.1 semver: 7.5.4
simple-update-notifier: 1.1.0 simple-update-notifier: 2.0.0
supports-color: 5.5.0 supports-color: 5.5.0
touch: 3.1.0 touch: 3.1.0
undefsafe: 2.0.5 undefsafe: 2.0.5
@ -11370,7 +11368,7 @@ packages:
dependencies: dependencies:
hosted-git-info: 4.1.0 hosted-git-info: 4.1.0
is-core-module: 2.12.1 is-core-module: 2.12.1
semver: 7.5.1 semver: 7.5.4
validate-npm-package-license: 3.0.4 validate-npm-package-license: 3.0.4
dev: true dev: true
@ -13705,6 +13703,7 @@ packages:
loose-envify: 1.4.0 loose-envify: 1.4.0
react: 18.2.0 react: 18.2.0
scheduler: 0.23.0 scheduler: 0.23.0
dev: true
/react-easy-crop@4.7.4(react-dom@18.2.0)(react@18.2.0): /react-easy-crop@4.7.4(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-oDi1375Jo/zuPUvo3oauxnNbfy8L4wsbmHD1KB2vT55fdgu+q8/K0w/rDWzy9jz4jfQ94Q9+3Yu366sDDFVmiA==} resolution: {integrity: sha512-oDi1375Jo/zuPUvo3oauxnNbfy8L4wsbmHD1KB2vT55fdgu+q8/K0w/rDWzy9jz4jfQ94Q9+3Yu366sDDFVmiA==}
@ -13784,7 +13783,7 @@ packages:
dependencies: dependencies:
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: true
/react-intl-universal@2.6.21(react@18.2.0): /react-intl-universal@2.6.21(react@18.2.0):
resolution: {integrity: sha512-JJqZMzdB6jd4f0NfGRc9XM/pZElxtrDCWC9pE2yIesDO70SL+jQgaFAucQmstPwYqCcjvLpCgOrjP7cIo4kJnw==} resolution: {integrity: sha512-JJqZMzdB6jd4f0NfGRc9XM/pZElxtrDCWC9pE2yIesDO70SL+jQgaFAucQmstPwYqCcjvLpCgOrjP7cIo4kJnw==}
@ -14002,6 +14001,7 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dependencies: dependencies:
loose-envify: 1.4.0 loose-envify: 1.4.0
dev: true
/reactcss@1.2.3(react@18.2.0): /reactcss@1.2.3(react@18.2.0):
resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==}
@ -14362,6 +14362,7 @@ packages:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
dependencies: dependencies:
loose-envify: 1.4.0 loose-envify: 1.4.0
dev: true
/schema-utils@3.1.2: /schema-utils@3.1.2:
resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==}
@ -14405,17 +14406,20 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true hasBin: true
/semver@7.0.0:
resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==}
hasBin: true
dev: true
/semver@7.5.1: /semver@7.5.1:
resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==} resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==}
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
dependencies: dependencies:
lru-cache: 6.0.0 lru-cache: 6.0.0
dev: false
/semver@7.5.4:
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
/send@0.18.0: /send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@ -14597,11 +14601,11 @@ packages:
is-arrayish: 0.3.2 is-arrayish: 0.3.2
dev: false dev: false
/simple-update-notifier@1.1.0: /simple-update-notifier@2.0.0:
resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
engines: {node: '>=8.10.0'} engines: {node: '>=10'}
dependencies: dependencies:
semver: 7.0.0 semver: 7.5.4
dev: true dev: true
/single-spa@5.9.4: /single-spa@5.9.4:
@ -16388,3 +16392,7 @@ packages:
- encoding - encoding
- supports-color - supports-color
dev: false dev: false
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false

View File

@ -472,7 +472,7 @@
"Python 软件包镜像源": "Python Software Package Mirror Source", "Python 软件包镜像源": "Python Software Package Mirror Source",
"Linux 软件包镜像源": "Linux Software Package Mirror Source", "Linux 软件包镜像源": "Linux Software Package Mirror Source",
"代理与镜像源二选一即可": "Either Proxy or Mirror Source can be chosen", "代理与镜像源二选一即可": "Either Proxy or Mirror Source can be chosen",
"代理地址, 支持HTTP(S)/SOCK5": "Proxy Address, supports HTTP(S)/SOCK5", "代理地址, 支持HTTP(S)/SOCK5, ip:port": "Proxy Address, supports HTTP(S)/SOCK5, ip:port",
"NPM 镜像源": "NPM Mirror Source", "NPM 镜像源": "NPM Mirror Source",
"PyPI 镜像源": "PyPI Mirror Source", "PyPI 镜像源": "PyPI Mirror Source",
"alpine linux 镜像源": "Alpine Linux Mirror Source" "alpine linux 镜像源": "Alpine Linux Mirror Source"

View File

@ -472,7 +472,7 @@
"Python 软件包镜像源": "Python 软件包镜像源", "Python 软件包镜像源": "Python 软件包镜像源",
"Linux 软件包镜像源": "Linux 软件包镜像源", "Linux 软件包镜像源": "Linux 软件包镜像源",
"代理与镜像源二选一即可": "代理与镜像源二选一即可", "代理与镜像源二选一即可": "代理与镜像源二选一即可",
"代理地址, 支持HTTP(S)/SOCK5": "代理地址, 支持HTTP(S)/SOCK5", "代理地址, 支持HTTP(S)/SOCK5, ip:port": "代理地址, 支持HTTP(S)/SOCK5, ip:port",
"NPM 镜像源": "NPM 镜像源", "NPM 镜像源": "NPM 镜像源",
"PyPI 镜像源": "PyPI 镜像源", "PyPI 镜像源": "PyPI 镜像源",
"alpine linux 镜像源": "alpine linux 镜像源" "alpine linux 镜像源": "alpine linux 镜像源"

View File

@ -15,6 +15,7 @@ import CodeMirror from '@uiw/react-codemirror';
import { useOutletContext } from '@umijs/max'; import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts'; import { SharedContext } from '@/layouts';
import { langs } from '@uiw/codemirror-extensions-langs'; import { langs } from '@uiw/codemirror-extensions-langs';
import { useHotkeys } from 'react-hotkeys-hook';
const Config = () => { const Config = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>(); const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
@ -68,6 +69,14 @@ const Config = () => {
getConfig(node.value); getConfig(node.value);
}; };
useHotkeys(
'meta+s',
(e) => {
updateConfig();
},
{ enableOnFormTags: ['textarea'], preventDefault: true },
);
useEffect(() => { useEffect(() => {
getFiles(); getFiles();
getConfig('config.sh'); getConfig('config.sh');

View File

@ -4,6 +4,15 @@ import { Button, InputNumber, Form, message, Input, Alert } from 'antd';
import config from '@/utils/config'; import config from '@/utils/config';
import { request } from '@/utils/http'; import { request } from '@/utils/http';
import './index.less'; import './index.less';
import Ansi from 'ansi-to-react';
import pick from 'lodash/pick';
const dataMap = {
'dependence-proxy': 'dependenceProxy',
'node-mirror': 'nodeMirror',
'python-mirror': 'pythonMirror',
'linux-mirror': 'linuxMirror',
};
const Dependence = () => { const Dependence = () => {
const [systemConfig, setSystemConfig] = useState<{ const [systemConfig, setSystemConfig] = useState<{
@ -13,6 +22,7 @@ const Dependence = () => {
linuxMirror?: string; linuxMirror?: string;
}>(); }>();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [log, setLog] = useState<string>('');
const getSystemConfig = () => { const getSystemConfig = () => {
request request
@ -27,9 +37,31 @@ const Dependence = () => {
}); });
}; };
const updateSystemConfig = () => { const updateSystemConfigStream = (path: keyof typeof dataMap) => {
setLog('执行中...');
request request
.put(`${config.apiPrefix}system/config`, systemConfig) .put<string>(
`${config.apiPrefix}system/config/${path}`,
pick(systemConfig, dataMap[path]),
{
responseType: 'stream',
},
)
.then((res) => {
setLog(() => res);
})
.catch((error: any) => {
console.log(error);
});
};
const updateSystemConfig = (path: keyof typeof dataMap) => {
setLog('');
request
.put(
`${config.apiPrefix}system/config/${path}`,
pick(systemConfig, dataMap[path]),
)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success(intl.get('更新成功')); message.success(intl.get('更新成功'));
@ -45,94 +77,120 @@ const Dependence = () => {
}, []); }, []);
return ( return (
<Form layout="vertical" form={form}> <div className="dependence-config-wrapper">
<Form.Item <Form layout="vertical" form={form}>
label={intl.get('代理')} <Form.Item
name="proxy" label={intl.get('代理')}
extra={intl.get('代理与镜像源二选一即可')} name="proxy"
extra={intl.get('代理与镜像源二选一即可')}
>
<Input.Group compact>
<Input
placeholder={intl.get('代理地址, 支持HTTP(S)/SOCK5, ip:port')}
style={{ width: 330 }}
value={systemConfig?.dependenceProxy}
onChange={(e) => {
setSystemConfig({
...systemConfig,
dependenceProxy: e.target.value,
});
}}
/>
<Button
type="primary"
onClick={() => {
updateSystemConfig('dependence-proxy');
}}
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={() => {
updateSystemConfigStream('node-mirror');
}}
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('python-mirror');
}}
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={() => {
updateSystemConfigStream('linux-mirror');
}}
style={{ width: 84 }}
>
{intl.get('确认')}
</Button>
</Input.Group>
</Form.Item>
</Form>
<pre
style={{
fontFamily: 'Source Code Pro',
zoom: 0.83,
maxHeight: '100%',
overflowY: 'auto'
}}
> >
<Input.Group compact> <Ansi>{log}</Ansi>
<Input </pre>
placeholder={intl.get('代理地址, 支持HTTP(S)/SOCK5')} </div>
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>
); );
}; };

View File

@ -27,3 +27,8 @@
} }
} }
} }
.dependence-config-wrapper {
display: flex;
gap: 40px;
}

View File

@ -21,6 +21,12 @@ import './index.less';
import { UploadOutlined } from '@ant-design/icons'; import { UploadOutlined } from '@ant-design/icons';
import Countdown from 'antd/lib/statistic/Countdown'; import Countdown from 'antd/lib/statistic/Countdown';
import useProgress from './progress'; import useProgress from './progress';
import pick from 'lodash/pick';
const dataMap = {
'log-remove-frequency': 'logRemoveFrequency',
'cron-concurrency': 'cronConcurrency',
};
const Other = ({ const Other = ({
systemInfo, systemInfo,
@ -32,7 +38,6 @@ const Other = ({
cronConcurrency?: number | null; cronConcurrency?: number | null;
}>(); }>();
const [form] = Form.useForm(); const [form] = Form.useForm();
const modalRef = useRef<any>();
const [exportLoading, setExportLoading] = useState(false); const [exportLoading, setExportLoading] = useState(false);
const showUploadProgress = useProgress(intl.get('上传')); const showUploadProgress = useProgress(intl.get('上传'));
const showDownloadProgress = useProgress(intl.get('下载')); const showDownloadProgress = useProgress(intl.get('下载'));
@ -80,9 +85,12 @@ const Other = ({
}); });
}; };
const updateSystemConfig = () => { const updateSystemConfig = (path: keyof typeof dataMap) => {
request request
.put(`${config.apiPrefix}system/config`, systemConfig) .put(
`${config.apiPrefix}system/config/${path}`,
pick(systemConfig, dataMap[path]),
)
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200) { if (code === 200) {
message.success(intl.get('更新成功')); message.success(intl.get('更新成功'));
@ -207,7 +215,9 @@ const Other = ({
/> />
<Button <Button
type="primary" type="primary"
onClick={updateSystemConfig} onClick={() => {
updateSystemConfig('log-remove-frequency');
}}
style={{ width: 84 }} style={{ width: 84 }}
> >
{intl.get('确认')} {intl.get('确认')}
@ -226,7 +236,9 @@ const Other = ({
/> />
<Button <Button
type="primary" type="primary"
onClick={updateSystemConfig} onClick={() => {
updateSystemConfig('cron-concurrency');
}}
style={{ width: 84 }} style={{ width: 84 }}
> >
{intl.get('确认')} {intl.get('确认')}