Compare commits

..
18 Commits
33 changed files with 3262 additions and 2868 deletions
+2 -2
View File
@@ -45,7 +45,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with: with:
version: latest version: '8.3.1'
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
@@ -99,7 +99,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with: with:
version: latest version: '8.3.1'
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
cache: 'pnpm' cache: 'pnpm'
+1 -1
View File
@@ -198,7 +198,7 @@ $ git clone git@github.com:whyour/qinglong.git
$ cd qinglong $ cd qinglong
$ cp .env.example .env $ cp .env.example .env
# Recommended use pnpm https://pnpm.io/zh/installation # Recommended use pnpm https://pnpm.io/zh/installation
$ npm install -g pnpm $ npm install -g pnpm@8.3.1
$ pnpm install $ pnpm install
$ pnpm start $ pnpm start
``` ```
+1 -1
View File
@@ -197,7 +197,7 @@ $ git clone git@github.com:whyour/qinglong.git
$ cd qinglong $ cd qinglong
$ cp .env.example .env $ cp .env.example .env
# 推荐使用 pnpm https://pnpm.io/zh/installation # 推荐使用 pnpm https://pnpm.io/zh/installation
$ npm install -g pnpm $ npm install -g pnpm@8.3.1
$ pnpm install $ pnpm install
$ pnpm start $ pnpm start
``` ```
+4 -3
View File
@@ -5,6 +5,7 @@ import { Logger } from 'winston';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs';
import { celebrate, Joi } from 'celebrate'; import { celebrate, Joi } from 'celebrate';
import { join } from 'path';
const route = Router(); const route = Router();
export default (app: Router) => { export default (app: Router) => {
@@ -41,11 +42,11 @@ export default (app: Router) => {
} }
if (req.params.file.includes('sample')) { if (req.params.file.includes('sample')) {
content = getFileContentByName( content = getFileContentByName(
`${config.samplePath}${req.params.file}`, join(config.samplePath, req.params.file),
); );
} else { } else {
content = getFileContentByName( content = getFileContentByName(
`${config.configPath}${req.params.file}`, join(config.configPath, req.params.file),
); );
} }
res.send({ code: 200, data: content }); res.send({ code: 200, data: content });
@@ -70,7 +71,7 @@ export default (app: Router) => {
if (config.blackFileList.includes(name)) { if (config.blackFileList.includes(name)) {
res.send({ code: 403, message: '文件无法访问' }); res.send({ code: 403, message: '文件无法访问' });
} }
const path = `${config.configPath}${name}`; const path = join(config.configPath, name);
fs.writeFileSync(path, content); fs.writeFileSync(path, content);
res.send({ code: 200, message: '保存成功' }); res.send({ code: 200, message: '保存成功' });
} catch (e) { } catch (e) {
+3 -3
View File
@@ -94,7 +94,7 @@ export default (app: Router) => {
path += '/'; path += '/';
} }
if (!path.startsWith('/')) { if (!path.startsWith('/')) {
path = `${config.scriptPath}${path}`; path = join(config.scriptPath, path);
} }
if (config.writePathList.every((x) => !path.startsWith(x))) { if (config.writePathList.every((x) => !path.startsWith(x))) {
return res.send({ return res.send({
@@ -124,7 +124,7 @@ export default (app: Router) => {
if (fs.existsSync(originFilePath)) { if (fs.existsSync(originFilePath)) {
fs.copyFileSync( fs.copyFileSync(
originFilePath, originFilePath,
`${config.bakPath}${originFilename.replace(/\//g, '')}`, join(config.bakPath, originFilename.replace(/\//g, '')),
); );
if (filename !== originFilename) { if (filename !== originFilename) {
fs.unlinkSync(originFilePath); fs.unlinkSync(originFilePath);
@@ -207,7 +207,7 @@ export default (app: Router) => {
let { filename } = req.body as { let { filename } = req.body as {
filename: string; filename: string;
}; };
const filePath = `${config.scriptPath}${filename}`; const filePath = join(config.scriptPath, filename);
// const stats = fs.statSync(filePath); // const stats = fs.statSync(filePath);
// res.set({ // res.set({
// 'Content-Type': 'application/octet-stream', //告诉浏览器这是一个二进制文件 // 'Content-Type': 'application/octet-stream', //告诉浏览器这是一个二进制文件
+5 -1
View File
@@ -170,6 +170,9 @@ export default (app: Router) => {
await systemService.run( await systemService.run(
{ ...req.body, logPath }, { ...req.body, logPath },
{ {
onStart: async (cp, startTime) => {
res.setHeader('QL-Task-Pid', `${cp.pid}`);
},
onEnd: async (cp, endTime, diff) => { onEnd: async (cp, endTime, diff) => {
res.end(); res.end();
}, },
@@ -195,7 +198,8 @@ export default (app: Router) => {
'/command-stop', '/command-stop',
celebrate({ celebrate({
body: Joi.object({ body: Joi.object({
command: Joi.string().required(), command: Joi.string().optional(),
pid: Joi.number().optional(),
}), }),
}), }),
async (req: Request, res: Response, next: NextFunction) => { async (req: Request, res: Response, next: NextFunction) => {
+1
View File
@@ -18,6 +18,7 @@ async function startServer() {
const server = app const server = app
.listen(config.port, () => { .listen(config.port, () => {
Logger.debug(`✌️ 后端服务启动成功!`); Logger.debug(`✌️ 后端服务启动成功!`);
process.send?.('ready');
}) })
.on('error', (err) => { .on('error', (err) => {
Logger.error(err); Logger.error(err);
+2 -1
View File
@@ -1,9 +1,10 @@
import { Sequelize, Transaction } from 'sequelize'; import { Sequelize, Transaction } from 'sequelize';
import config from '../config/index'; import config from '../config/index';
import { join } from 'path';
export const sequelize = new Sequelize({ export const sequelize = new Sequelize({
dialect: 'sqlite', dialect: 'sqlite',
storage: `${config.dbPath}database.sqlite`, storage: join(config.dbPath, 'database.sqlite'),
logging: false, logging: false,
retry: { retry: {
max: 10, max: 10,
+23 -1
View File
@@ -14,19 +14,41 @@ import handler from 'serve-handler';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { EnvModel } from '../data/env'; import { EnvModel } from '../data/env';
import { errors } from 'celebrate'; import { errors } from 'celebrate';
import path from 'path';
import { createProxyMiddleware } from 'http-proxy-middleware';
export default ({ app }: { app: Application }) => { export default ({ app }: { app: Application }) => {
app.enable('trust proxy'); app.enable('trust proxy');
app.use(cors()); app.use(cors());
app.use(`${config.api.prefix}/static`, express.static(config.uploadPath)); app.use(`${config.api.prefix}/static`, express.static(config.uploadPath));
app.use(
'/api/public',
createProxyMiddleware({
target: `http://localhost:${config.publicPort}/api`,
changeOrigin: true,
pathRewrite: { '/api/public': '' },
}),
);
app.use((req, res, next) => { app.use((req, res, next) => {
if (req.path.startsWith('/api') || req.path.startsWith('/open')) { if (req.path.startsWith('/api') || req.path.startsWith('/open')) {
next(); next();
} else { } else {
return handler(req, res, { return handler(req, res, {
public: 'static/dist', public: path.join(config.rootPath, 'static/dist'),
rewrites: [{ source: '**', destination: '/index.html' }], rewrites: [{ source: '**', destination: '/index.html' }],
headers: [
{
source: 'index.html',
headers: [
{
key: 'Cache-Control',
value: 'no-cache',
},
],
},
],
}); });
} }
}); });
+4 -2
View File
@@ -4,6 +4,7 @@ import ScheduleService from '../services/schedule';
import SubscriptionService from '../services/subscription'; import SubscriptionService from '../services/subscription';
import config from '../config'; import config from '../config';
import { fileExist } from '../config/util'; import { fileExist } from '../config/util';
import { join } from 'path';
export default async () => { export default async () => {
const systemService = Container.get(SystemService); const systemService = Container.get(SystemService);
@@ -11,8 +12,9 @@ export default async () => {
const subscriptionService = Container.get(SubscriptionService); const subscriptionService = Container.get(SubscriptionService);
// 生成内置token // 生成内置token
let tokenCommand = `tsx ${config.rootPath}/back/token.ts`; let tokenCommand = `tsx ${join(config.rootPath, 'back/token.ts')}`;
const tokenFile = `${config.rootPath}static/build/token.js`; const tokenFile = join(config.rootPath, 'static/build/token.js');
if (await fileExist(tokenFile)) { if (await fileExist(tokenFile)) {
tokenCommand = `node ${tokenFile}`; tokenCommand = `node ${tokenFile}`;
} }
+8 -2
View File
@@ -9,8 +9,14 @@ export default async ({ server }: { server: Server }) => {
process.on('SIGINT', () => { process.on('SIGINT', () => {
Logger.info('✌️ Server need close'); Logger.info('✌️ Server need close');
server.close(() => { server.close(() => {
Logger.info('✌️ Server closed'); setTimeout(() => {
process.exit(0); process.exit();
}, 10000);
}); });
setTimeout(() => {
console.log('Forcing server close !!!');
process.exit(1);
}, 15000);
}); });
}; };
+3 -3
View File
@@ -1,5 +1,4 @@
import express from 'express'; import express from 'express';
import { exec } from 'child_process';
import Logger from './loaders/logger'; import Logger from './loaders/logger';
import config from './config'; import config from './config';
import { HealthClient } from './protos/health'; import { HealthClient } from './protos/health';
@@ -14,9 +13,9 @@ const client = new HealthClient(
app.get('/api/health', (req, res) => { app.get('/api/health', (req, res) => {
client.check({ service: 'cron' }, (err, response) => { client.check({ service: 'cron' }, (err, response) => {
if (err) { if (err) {
return res.status(500).send({ error: err }); return res.status(500).send({ code: 500, error: err });
} }
return res.status(200).send({ data: response }); return res.status(200).send({ code: 200, data: response });
}); });
}); });
@@ -26,6 +25,7 @@ app
await require('./loaders/db').default(); await require('./loaders/db').default();
Logger.debug(`✌️ 公共服务启动成功!`); Logger.debug(`✌️ 公共服务启动成功!`);
process.send?.('ready');
}) })
.on('error', (err) => { .on('error', (err) => {
Logger.error(err); Logger.error(err);
+1
View File
@@ -16,5 +16,6 @@ server.bindAsync(
() => { () => {
server.start(); server.start();
Logger.debug(`✌️ 定时服务启动成功!`); Logger.debug(`✌️ 定时服务启动成功!`);
process.send?.('ready');
}, },
); );
+13 -4
View File
@@ -190,13 +190,22 @@ export default class SystemService {
); );
} }
public async stop({ command }: { command: string }) { public async stop({ command, pid }: { command: string; pid: number }) {
if (!pid && !command) {
return { code: 400, message: '参数错误' };
}
if (pid) {
await killTask(pid);
return { code: 200 };
}
if (!command.startsWith(TASK_COMMAND)) { if (!command.startsWith(TASK_COMMAND)) {
command = `${TASK_COMMAND} ${command}`; command = `${TASK_COMMAND} ${command}`;
} }
const pid = await getPid(command); const _pid = await getPid(command);
if (pid) { if (_pid) {
await killTask(pid); await killTask(_pid);
return { code: 200 }; return { code: 200 };
} else { } else {
return { code: 400, message: '任务未找到' }; return { code: 400, message: '任务未找到' };
+20 -11
View File
@@ -1,6 +1,11 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import { createRandomString, getNetIp, getPlatform } from '../config/util'; import {
createRandomString,
fileExist,
getNetIp,
getPlatform,
} from '../config/util';
import config from '../config'; import config from '../config';
import * as fs from 'fs'; import * as fs from 'fs';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
@@ -205,17 +210,16 @@ export default class UserService {
} }
private initAuthInfo() { private initAuthInfo() {
const newPassword = createRandomString(16, 22);
fs.writeFileSync( fs.writeFileSync(
config.authConfigFile, config.authConfigFile,
JSON.stringify({ JSON.stringify({
username: 'admin', username: 'admin',
password: newPassword, password: 'admin',
}), }),
); );
return { return {
code: 100, code: 100,
message: '已初始化密码,请前往auth.json查看并重新登录', message: '未找到认证文件,重新初始化',
}; };
} }
@@ -240,13 +244,18 @@ export default class UserService {
return { code: 200, data: avatar, message: '更新成功' }; return { code: 200, data: avatar, message: '更新成功' };
} }
public getUserInfo(): Promise<any> { public async getUserInfo(): Promise<any> {
return new Promise((resolve) => { const authFileExist = await fileExist(config.authConfigFile);
fs.readFile(config.authConfigFile, 'utf8', (err, data) => { if (!authFileExist) {
if (err) console.log(err); fs.writeFileSync(
resolve(JSON.parse(data)); config.authConfigFile,
}); JSON.stringify({
}); username: 'admin',
password: 'admin',
}),
);
}
return this.getAuthInfo();
} }
public initTwoFactor() { public initTwoFactor() {
+6 -5
View File
@@ -3,9 +3,9 @@ COPY package.json .npmrc pnpm-lock.yaml /tmp/build/
RUN set -x \ RUN set -x \
&& apk update \ && apk update \
&& apk add nodejs npm git \ && apk add nodejs npm git \
&& npm i -g pnpm \ && npm i -g pnpm@8.3.1 \
&& cd /tmp/build \ && cd /tmp/build \
&& pnpm install --prod && pnpm --registry https://registry.npmmirror.com install --prod
FROM python:3.10-alpine FROM python:3.10-alpine
@@ -29,7 +29,6 @@ RUN set -x \
&& apk upgrade \ && apk upgrade \
&& apk --no-cache add -f bash \ && apk --no-cache add -f bash \
coreutils \ coreutils \
moreutils \
git \ git \
curl \ curl \
wget \ wget \
@@ -41,6 +40,7 @@ RUN set -x \
jq \ jq \
openssh \ openssh \
procps \ procps \
netcat-openbsd \
npm \ npm \
&& rm -rf /var/cache/apk/* \ && rm -rf /var/cache/apk/* \
&& apk update \ && apk update \
@@ -49,7 +49,8 @@ RUN set -x \
&& git config --global user.email "qinglong@@users.noreply.github.com" \ && git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \ && git config --global user.name "qinglong" \
&& git config --global http.postBuffer 524288000 \ && git config --global http.postBuffer 524288000 \
&& npm install -g pnpm \ && npm install -g pnpm@8.3.1 \
&& cd && pnpm config set registry https://registry.npmmirror.com \
&& pnpm add -g pm2 tsx \ && pnpm add -g pm2 tsx \
&& rm -rf /root/.pnpm-store \ && rm -rf /root/.pnpm-store \
&& rm -rf /root/.local/share/pnpm/store \ && rm -rf /root/.local/share/pnpm/store \
@@ -71,7 +72,7 @@ COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/
WORKDIR ${QL_DIR} WORKDIR ${QL_DIR}
HEALTHCHECK --interval=5s --timeout=2s --retries=10 \ HEALTHCHECK --interval=5s --timeout=2s --retries=20 \
CMD curl -sf http://127.0.0.1:5400/api/health || exit 1 CMD curl -sf http://127.0.0.1:5400/api/health || exit 1
ENTRYPOINT ["./docker/docker-entrypoint.sh"] ENTRYPOINT ["./docker/docker-entrypoint.sh"]
+14 -18
View File
@@ -10,12 +10,21 @@ echo -e "======================1. 检测配置文件========================\n"
make_dir /etc/nginx/conf.d make_dir /etc/nginx/conf.d
make_dir /run/nginx make_dir /run/nginx
init_nginx init_nginx
fix_config
pm2 l &>/dev/null pm2 l &>/dev/null
pm2 flush &>/dev/null
echo -e "======================2. 安装依赖========================\n" echo -e "======================2. 安装依赖========================\n"
patch_version patch_version
if [[ $PipMirror ]]; then
pip3 config set global.index-url $PipMirror
fi
current_npm_registry=$(cd && pnpm config get registry)
is_equal_registry=$(echo $current_npm_registry | grep "${NpmMirror}")
if [[ "$is_equal_registry" == "" ]]; then
cd && pnpm config set registry $NpmMirror
pnpm install -g
fi
update_depend update_depend
echo echo
@@ -23,36 +32,23 @@ echo -e "======================3. 启动nginx========================\n"
nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf nginx -s reload 2>/dev/null || nginx -c /etc/nginx/nginx.conf
echo -e "nginx启动成功...\n" echo -e "nginx启动成功...\n"
echo -e "======================4. 启动面板监控========================\n" echo -e "======================4. 启动pm2服务========================\n"
pm2 delete public &>/dev/null reload_pm2
pm2 start $dir_static/build/public.js -n public --source-map-support --time
echo -e "监控服务启动成功...\n"
echo -e "======================5. 启动主服务========================\n"
pm2 delete panel &>/dev/null
pm2 start $dir_static/build/app.js -n panel --source-map-support --time
echo -e "主服务启动成功...\n"
echo -e "======================6. 启动定时服务========================\n"
pm2 delete schedule &>/dev/null
pm2 start $dir_static/build/schedule/index.js -n schedule --source-map-support --time
echo -e "定时任务启动成功...\n"
if [[ $AutoStartBot == true ]]; then if [[ $AutoStartBot == true ]]; then
echo -e "======================7. 启动bot========================\n" echo -e "======================5. 启动bot========================\n"
nohup ql -l bot >$dir_log/bot.log 2>&1 & nohup ql -l bot >$dir_log/bot.log 2>&1 &
echo -e "bot后台启动中...\n" echo -e "bot后台启动中...\n"
fi fi
if [[ $EnableExtraShell == true ]]; then if [[ $EnableExtraShell == true ]]; then
echo -e "======================8. 执行自定义脚本========================\n" echo -e "====================6. 执行自定义脚本========================\n"
nohup ql -l extra >$dir_log/extra.log 2>&1 & nohup ql -l extra >$dir_log/extra.log 2>&1 &
echo -e "自定义脚本后台执行中...\n" echo -e "自定义脚本后台执行中...\n"
fi fi
echo -e "############################################################\n" echo -e "############################################################\n"
echo -e "容器启动成功..." echo -e "容器启动成功..."
echo -e "\n请先访问5700端口,登录成功面板之后再执行添加定时任务..."
echo -e "############################################################\n" echo -e "############################################################\n"
crond -f >/dev/null crond -f >/dev/null
+3
View File
@@ -22,6 +22,7 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://publicApi/api/; proxy_pass http://publicApi/api/;
proxy_buffering off;
} }
location QL_BASE_URL/api/ { location QL_BASE_URL/api/ {
@@ -29,6 +30,7 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://baseApi/api/; proxy_pass http://baseApi/api/;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; proxy_set_header Connection $connection_upgrade;
@@ -39,6 +41,7 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://baseApi/open/; proxy_pass http://baseApi/open/;
proxy_buffering off;
} }
gzip on; gzip on;
+31
View File
@@ -0,0 +1,31 @@
module.exports = {
apps: [
{
name: 'schedule',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
source_map_support: true,
time: true,
script: 'static/build/schedule/index.js',
},
{
name: 'public',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
source_map_support: true,
time: true,
script: 'static/build/public.js',
},
{
name: 'panel',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
source_map_support: true,
time: true,
script: 'static/build/app.js',
},
],
};
+2 -1
View File
@@ -72,6 +72,7 @@
"form-data": "^4.0.0", "form-data": "^4.0.0",
"got": "^11.8.2", "got": "^11.8.2",
"hpagent": "^0.1.2", "hpagent": "^0.1.2",
"http-proxy-middleware": "^2.0.6",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
@@ -86,7 +87,7 @@
"sequelize": "^6.25.5", "sequelize": "^6.25.5",
"serve-handler": "^6.1.3", "serve-handler": "^6.1.3",
"sockjs": "^0.3.24", "sockjs": "^0.3.24",
"sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.2", "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
"toad-scheduler": "^1.6.0", "toad-scheduler": "^1.6.0",
"typedi": "^0.10.0", "typedi": "^0.10.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
+3013 -2720
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
create_token() { create_token() {
local token_command="tsx ${dir_root}/back/token.ts" local token_command="tsx ${dir_root}/back/token.ts"
local token_file="${dir_root}static/build/token.js" local token_file="${dir_root}/static/build/token.js"
if [[ -f $token_file ]]; then if [[ -f $token_file ]]; then
token_command="node ${token_file}" token_command="node ${token_file}"
fi fi
+12 -8
View File
@@ -74,19 +74,23 @@ check_pm2() {
fi fi
} }
start_public() {
echo -e "=====> 启动公开服务\n"
pm2 delete public --source-map-support --time &>/dev/null
pm2 start $dir_static/build/public.js -n public --source-map-support --time &>/dev/null
}
main() { main() {
echo -e "=====> 开始检测" echo -e "=====> 开始检测"
npm i -g pnpm npm i -g pnpm@8.3.1
patch_version patch_version
apk add procps netcat-openbsd
if [[ $PipMirror ]]; then
pip3 config set global.index-url $PipMirror
fi
if [[ $NpmMirror ]]; then
cd && pnpm config set registry $NpmMirror
pnpm install -g
fi
pnpm add -g pm2 tsx pnpm add -g pm2 tsx
reset_env reset_env
start_public
copy_dep copy_dep
check_ql check_ql
check_nginx check_nginx
+29 -54
View File
@@ -18,6 +18,7 @@ dir_update_log=$dir_log/update
ql_static_repo=$dir_repo/static ql_static_repo=$dir_repo/static
## 文件 ## 文件
file_ecosystem_js=$dir_root/ecosystem.config.js
file_config_sample=$dir_sample/config.sample.sh file_config_sample=$dir_sample/config.sample.sh
file_env=$dir_config/env.sh file_env=$dir_config/env.sh
file_sharecode=$dir_config/sharecode.sh file_sharecode=$dir_config/sharecode.sh
@@ -98,6 +99,9 @@ set_proxy() {
unset_proxy() { unset_proxy() {
unset http_proxy unset http_proxy
unset https_proxy unset https_proxy
unset ftp_proxy
unset all_proxy
unset no_proxy
} }
make_dir() { make_dir() {
@@ -262,17 +266,6 @@ npm_install_sub() {
fi fi
} }
npm_install_1() {
local dir_current=$(pwd)
local dir_work=$1
cd $dir_work
echo -e "运行 pnpm install...\n"
npm_install_sub
[[ $? -ne 0 ]] && echo -e "\nnpm install 运行不成功,请进入 $dir_work 目录后手动运行 pnpm install...\n"
cd $dir_current
}
npm_install_2() { npm_install_2() {
local dir_current=$(pwd) local dir_current=$(pwd)
local dir_work=$1 local dir_work=$1
@@ -280,10 +273,6 @@ npm_install_2() {
cd $dir_work cd $dir_work
echo -e "安装 $dir_work 依赖包...\n" echo -e "安装 $dir_work 依赖包...\n"
npm_install_sub npm_install_sub
if [[ $? -ne 0 ]]; then
echo -e "\n安装 $dir_work 的依赖包运行不成功,再次尝试一遍...\n"
npm_install_1 $dir_work
fi
cd $dir_current cd $dir_current
} }
@@ -315,11 +304,13 @@ git_clone_scripts() {
echo -e "开始克隆仓库 $url$dir\n" echo -e "开始克隆仓库 $url$dir\n"
set_proxy "$proxy" set_proxy "$proxy"
git clone --depth=1 $part_cmd $url $dir git clone --depth=1 $part_cmd $url $dir
exit_status=$? exit_status=$?
unset_proxy
reset_branch "$branch" reset_branch "$branch" "$dir"
unset_proxy
} }
git_pull_scripts() { git_pull_scripts() {
@@ -329,19 +320,25 @@ git_pull_scripts() {
local proxy="$3" local proxy="$3"
cd $dir_work cd $dir_work
echo -e "开始更新仓库:$dir_work" echo -e "开始更新仓库:$dir_work"
set_proxy "$proxy"
if [[ ! $branch ]]; then
branch=$(cd $dir_work && git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)
fi
local pre_commit_id=$(git rev-parse --short HEAD) local pre_commit_id=$(git rev-parse --short HEAD)
set_proxy "$proxy" reset_branch "$branch" "$dir_work"
git fetch --depth=1 --all
git pull --depth=1 &>/dev/null
exit_status=$?
unset_proxy
reset_branch "$branch" git fetch --depth 1 origin $branch
exit_status=$?
reset_branch "$branch" "$dir_work"
local cur_commit_id=$(git rev-parse --short HEAD) local cur_commit_id=$(git rev-parse --short HEAD)
if [[ $cur_commit_id != $pre_commit_id ]]; then if [[ $cur_commit_id != $pre_commit_id ]]; then
exit_status=0 exit_status=0
fi fi
unset_proxy
cd $dir_current cd $dir_current
} }
@@ -359,19 +356,16 @@ reset_romote_url() {
git init git init
git remote add origin $url &>/dev/null git remote add origin $url &>/dev/null
fi fi
cd $dir_current cd $dir_current
} }
reset_branch() { reset_branch() {
local branch="$1" local branch="$1"
local part_cmd="HEAD" local part_cmd="origin/${branch}"
if [[ $branch ]]; then git remote set-branches origin $branch
part_cmd="origin/${branch}"
git checkout -B "$branch" &>/dev/null
git branch --set-upstream-to=$part_cmd $branch &>/dev/null
fi
git reset --hard $part_cmd &>/dev/null git reset --hard $part_cmd &>/dev/null
git checkout -b $branch $part_cmd &>/dev/null
} }
random_range() { random_range() {
@@ -381,16 +375,11 @@ random_range() {
} }
reload_pm2() { reload_pm2() {
pm2 l &>/dev/null cd $dir_root
# 代理会影响 grpc 服务
unset_proxy
pm2 flush &>/dev/null pm2 flush &>/dev/null
pm2 startOrGracefulReload $file_ecosystem_js
echo -e "启动面板服务\n"
pm2 delete panel --source-map-support --time &>/dev/null
pm2 start $dir_static/build/app.js -n panel --source-map-support --time &>/dev/null
echo -e "启动定时服务\n"
pm2 delete schedule --source-map-support --time &>/dev/null
pm2 start $dir_static/build/schedule/index.js -n schedule --source-map-support --time &>/dev/null
} }
diff_time() { diff_time() {
@@ -440,19 +429,6 @@ format_timestamp() {
} }
patch_version() { patch_version() {
# 兼容pnpm@7
pnpm setup &>/dev/null
source ~/.bashrc
apk add procps
if [[ $PipMirror ]]; then
pip3 config set global.index-url $PipMirror
fi
if [[ $NpmMirror ]]; then
cd && pnpm config set registry $NpmMirror
pnpm install -g --force
fi
git config --global pull.rebase false git config --global pull.rebase false
cp -f $dir_root/.env.example $dir_root/.env cp -f $dir_root/.env.example $dir_root/.env
@@ -493,7 +469,7 @@ init_nginx() {
cp -fv $nginx_conf /etc/nginx/nginx.conf cp -fv $nginx_conf /etc/nginx/nginx.conf
cp -fv $nginx_app_conf /etc/nginx/conf.d/front.conf cp -fv $nginx_app_conf /etc/nginx/conf.d/front.conf
sed -i "s,QL_BASE_URL,${qlBaseUrl},g" /etc/nginx/conf.d/front.conf sed -i "s,QL_BASE_URL,${qlBaseUrl},g" /etc/nginx/conf.d/front.conf
ipv6=$(ip a | grep inet6) ipv6=$(ip a | grep inet6)
ipv6Str="" ipv6Str=""
if [[ $ipv6 ]]; then if [[ $ipv6 ]]; then
@@ -536,6 +512,5 @@ init_env
detect_termux detect_termux
detect_macos detect_macos
define_cmd define_cmd
fix_config
import_config $1 2>$task_error_log_path import_config $1 2>$task_error_log_path
+4
View File
@@ -257,6 +257,9 @@ update_qinglong() {
cp -f $file_config_sample $dir_config/config.sample.sh cp -f $file_config_sample $dir_config/config.sample.sh
update_depend update_depend
[[ -f $dir_root/package.json ]] && ql_depend_new=$(cat $dir_root/package.json)
[[ "$ql_depend_old" != "$ql_depend_new" ]] && npm_install_2 $dir_root
update_qinglong_static "$1" "$primary_branch" update_qinglong_static "$1" "$primary_branch"
else else
echo -e "\n更新青龙源文件失败,请检查网络...\n" echo -e "\n更新青龙源文件失败,请检查网络...\n"
@@ -467,6 +470,7 @@ main() {
case $p1 in case $p1 in
update) update)
fix_config
eval update_qinglong "$2" $cmd eval update_qinglong "$2" $cmd
;; ;;
extra) extra)
+19 -6
View File
@@ -98,8 +98,7 @@ export default function () {
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
}) });
.finally(() => setInitLoading(false));
}; };
const getUser = (needLoading = true) => { const getUser = (needLoading = true) => {
@@ -120,6 +119,22 @@ export default function () {
}); });
}; };
const getHealthStatus = () => {
request
.get(`${config.apiPrefix}public/health`)
.then((res) => {
if (res?.data?.status === 1) {
getSystemInfo();
} else {
history.push('/error');
}
})
.catch((error) => {
history.push('/error');
})
.finally(() => setInitLoading(false));
};
const reloadUser = (needLoading = false) => { const reloadUser = (needLoading = false) => {
getUser(needLoading); getUser(needLoading);
}; };
@@ -131,10 +146,8 @@ export default function () {
}, [location.pathname]); }, [location.pathname]);
useEffect(() => { useEffect(() => {
if (!systemInfo) { getHealthStatus();
getSystemInfo(); }, []);
}
}, [systemInfo]);
useEffect(() => { useEffect(() => {
if (theme === 'vs-dark') { if (theme === 'vs-dark') {
+1 -1
View File
@@ -246,7 +246,7 @@ const Dependence = () => {
}) })
.then(({ code, data }) => { .then(({ code, data }) => {
if (code === 200 && force) { if (code === 200 && force) {
const i = value.findIndex((x) => x.id === data.data[0].id); const i = value.findIndex((x) => x.id === data[0].id);
if (i !== -1) { if (i !== -1) {
const result = [...value]; const result = [...value];
result.splice(i, 1); result.splice(i, 1);
+1 -1
View File
@@ -394,7 +394,7 @@ const Env = () => {
if (code === 200) { if (code === 200) {
const newData = [...value]; const newData = [...value];
newData.splice(dragIndex, 1); newData.splice(dragIndex, 1);
newData.splice(hoverIndex, 0, { ...dragRow, ...data.data }); newData.splice(hoverIndex, 0, { ...dragRow, ...data });
setValue([...newData]); setValue([...newData]);
} }
}); });
+4 -2
View File
@@ -13,7 +13,8 @@
.code-box { .code-box {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 80%; width: 80vw;
height: 90vh;
margin: 16px; margin: 16px;
background-color: #ffffff; background-color: #ffffff;
border: 1px solid rgba(5, 5, 5, 0.06); border: 1px solid rgba(5, 5, 5, 0.06);
@@ -44,9 +45,10 @@
} }
.log { .log {
height: calc(100vh - 150px); height: calc(90vh - 150px);
overflow-y: auto; overflow-y: auto;
padding: 12px; padding: 12px;
white-space: pre-line;
} }
} }
} }
+25 -4
View File
@@ -17,7 +17,7 @@ const Error = () => {
needLoading && setLoading(true); needLoading && setLoading(true);
request request
.get(`${config.apiPrefix}public/health`) .get(`${config.apiPrefix}public/health`)
.then(({ status, error }) => { .then(({ error, status }) => {
if (status === 1) { if (status === 1) {
return reloadUser(); return reloadUser();
} }
@@ -27,7 +27,6 @@ const Error = () => {
} }
retryTimes.current += 1; retryTimes.current += 1;
setTimeout(() => { setTimeout(() => {
reloadUser();
getLog(false); getLog(false);
}, 3000); }, 3000);
}) })
@@ -53,10 +52,32 @@ const Error = () => {
<div className="browser-markup"></div> <div className="browser-markup"></div>
<Alert <Alert
type="error" type="error"
message="服务启动超时,请检查如下日志或者进入容器执行 ql -l check 后刷新再试" message={
<Typography.Title level={5} type="danger">
</Typography.Title>
}
description={
<Typography.Text type="danger">
<div>
<Typography.Link href="https://github.com/whyour/qinglong/issues/new?assignees=&labels=&template=bug_report.yml">
issue
</Typography.Link>
</div>
<div>
1. 宿 docker run --rm -v
/var/run/docker.sock:/var/run/docker.sock
containrrr/watchtower -cR &lt;&gt;
</div>
<div>2. ql -l checkql -l update</div>
</Typography.Text>
}
banner banner
/> />
<Typography.Paragraph className="log">{data}</Typography.Paragraph> <Typography.Paragraph code className="log">
{data}
</Typography.Paragraph>
</div> </div>
) : ( ) : (
<PageLoading tip="启动中,请稍后..." /> <PageLoading tip="启动中,请稍后..." />
+1 -1
View File
@@ -219,7 +219,7 @@ const Initialization = () => {
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
history.push('/login'); window.location.reload();
}} }}
> >
+1 -1
View File
@@ -91,7 +91,7 @@ export default {
{ value: 'aibotk', label: '智能微秘书' }, { value: 'aibotk', label: '智能微秘书' },
{ value: 'iGot', label: 'IGot' }, { value: 'iGot', label: 'IGot' },
{ value: 'pushPlus', label: 'PushPlus' }, { value: 'pushPlus', label: 'PushPlus' },
{ value: 'chat', label: '群chat' }, { value: 'chat', label: '群chat' },
{ value: 'email', label: '邮箱' }, { value: 'email', label: '邮箱' },
{ value: 'lark', label: '飞书机器人' }, { value: 'lark', label: '飞书机器人' },
{ value: 'webhook', label: '自定义通知' }, { value: 'webhook', label: '自定义通知' },
+4 -10
View File
@@ -1,11 +1,5 @@
version: 2.15.13 version: 2.15.14
changeLogLink: https://t.me/jiao_long/373 changeLogLink: https://t.me/jiao_long/374
changeLog: | changeLog: |
1. 增加运行、停止指定命令接口 system/command-runsystem/command-stop 1. 接口 system/command-run 返回响应头 QL-Task-Pidsystem/command-stop 支持 pid 参数
2. 增加容器健康检查 2. 其他 bug 修复
3. 移除执行任务默认超时时间
4. 修改 task 命令生成日志逻辑和关联任务查询
5. 修复更新任务、环境变量、依赖、订阅状态丢失
6. 修改系统通知错误提示
7. 修复系统通知 gotify 配置
8. 其他 bug 修复