Compare commits

..
30 Commits
Author SHA1 Message Date
whyour 86e3d8736b 更新版本 v2.15.15 2023-05-28 20:06:15 +08:00
whyour 85d32d00e1 修改打包 latest 镜像分支 2023-05-28 20:06:05 +08:00
whyour 6a52e2f804 修复 env.js 路径 2023-05-27 23:05:46 +08:00
whyour b4cb72c5d0 修改错误页面提示 2023-05-27 11:35:30 +08:00
whyour 79fd4ec58f 修复 nginx 变量替换 2023-05-26 17:08:40 +08:00
jzksnsjswkwandGitHub 49ed5c6632 修复 可执行文件 提示找不到文件的问题 (#1943) 2023-05-26 15:17:47 +08:00
whyour ffba7ee4a1 增加定时任务服务启动失败日志 2023-05-25 21:45:28 +08:00
whyour 91d080472f 修复环境变量接口前缀 2023-05-24 23:46:07 +08:00
whyour 826f214f0f 修复设置通知错误时提示不清晰 2023-05-24 23:37:44 +08:00
whyour ca150dc6a6 修复 docker-compose 配置 2023-05-22 20:20:46 +08:00
whyour ae5db60211 修复筛选可能报错 2023-05-21 12:29:29 +08:00
whyour 490bdc15f6 支持非根目录部署 2023-05-19 01:10:33 +08:00
whyour 1377fe31aa 更新版本 v2.15.14 2023-05-15 23:44:42 +08:00
whyour 3d1cf6bf2e 修改群晖名称 2023-05-15 23:44:32 +08:00
whyour 6ac755b478 修复后端 html 缓存 2023-05-15 22:34:06 +08:00
whyour e70fa83074 修复获取配置文件参数 2023-05-14 20:53:45 +08:00
whyour 41521f1d08 修复 token 任务脚本路径,容器启动 bot 2023-05-13 01:10:49 +08:00
whyour 8b7bef097c 修复拉取仓库逻辑 2023-05-12 18:28:01 +08:00
whyour 618756d4a9 修复设置代理时,服务启动失败 2023-05-10 23:17:05 +08:00
whyour 70d0c71414 修复错误日志样式 2023-05-10 23:01:51 +08:00
whyour 65eb9c806d 更新 sqlite 2023-05-10 21:58:11 +08:00
whyour 50718ba714 修复 public 服务转发 2023-05-10 01:07:46 +08:00
whyour 7c3bb90d12 修复删除依赖和移动环境变量接口数据结构 2023-05-08 21:29:25 +08:00
whyour f66390eabd 修改初始系统状态逻辑已经错误页面 2023-05-07 23:40:37 +08:00
whyour 481646501c 修复订阅更新仓库失败 2023-05-06 22:58:05 +08:00
whyour 5baa8e4391 增加 pm2 平滑重启 2023-05-06 00:09:03 +08:00
whyour 3d11210aeb pnpm 版本降至 8.3.1 2023-05-05 00:26:23 +08:00
whyour e1863407f2 修复 npm 源判断 2023-05-04 22:23:55 +08:00
whyour 0c28b63ab7 修改容器启动流程 2023-05-04 22:07:39 +08:00
whyour 8b8bd279c6 修复 nginx 转发stream接口失效,停止运行命令支持 pid 参数 2023-05-04 21:28:14 +08:00
47 changed files with 3472 additions and 2952 deletions
+6 -5
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:
@@ -85,8 +85,6 @@ jobs:
git push --force --quiet gitlab ${GITHUB_BRANCH}:${GITHUB_BRANCH} git push --force --quiet gitlab ${GITHUB_BRANCH}:${GITHUB_BRANCH}
build: build:
if: github.ref != 'refs/heads/master'
needs: build-static needs: build-static
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -99,7 +97,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'
@@ -131,11 +129,14 @@ jobs:
ghcr.io/${{ github.repository }} ghcr.io/${{ github.repository }}
# generate Docker tags based on the following events/attributes # generate Docker tags based on the following events/attributes
# nightly, master, pr-2, 1.2.3, 1.2, 1 # nightly, master, pr-2, 1.2.3, 1.2, 1
flavor: |
latest=false
tags: | tags: |
type=schedule,pattern=nightly type=schedule,pattern=nightly
type=edge type=edge
type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=ref,event=branch,enable=${{ github.ref != format('refs/heads/{0}', 'master') }}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
+7 -4
View File
@@ -1,26 +1,28 @@
import { defineConfig } from '@umijs/max'; import { defineConfig } from '@umijs/max';
const CompressionPlugin = require('compression-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin');
const baseUrl = process.env.QlBaseUrl || '/';
export default defineConfig({ export default defineConfig({
hash: true, hash: true,
antd: {}, antd: {},
outputPath: 'static/dist', outputPath: 'static/dist',
fastRefresh: true, fastRefresh: true,
favicons: ['./images/favicon.svg'], favicons: [`https://qn.whyour.cn/favicon.svg`],
mfsu: { mfsu: {
strategy: 'eager', strategy: 'eager',
}, },
publicPath: process.env.NODE_ENV === 'production' ? './' : '/', publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
proxy: { proxy: {
'/api/public': { [`${baseUrl}api/public`]: {
target: 'http://127.0.0.1:5400/', target: 'http://127.0.0.1:5400/',
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/api/public': '/api/' }, pathRewrite: { [`^${baseUrl}api/public`]: '/api' },
}, },
'/api': { [`${baseUrl}api`]: {
target: 'http://127.0.0.1:5600/', target: 'http://127.0.0.1:5600/',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
pathRewrite: { [`^${baseUrl}api`]: '/api' },
}, },
}, },
chainWebpack: ((config: any) => { chainWebpack: ((config: any) => {
@@ -38,6 +40,7 @@ export default defineConfig({
'react-dom': 'window.ReactDOM', 'react-dom': 'window.ReactDOM',
}, },
headScripts: [ headScripts: [
`./api/env.js`,
'https://gw.alipayobjects.com/os/lib/react/18.2.0/umd/react.production.min.js', 'https://gw.alipayobjects.com/os/lib/react/18.2.0/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/18.2.0/umd/react-dom.production.min.js', 'https://gw.alipayobjects.com/os/lib/react-dom/18.2.0/umd/react-dom.production.min.js',
], ],
+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
``` ```
+5 -1
View File
@@ -59,6 +59,8 @@ podman run -dit \
--network bridge \ --network bridge \
-v $PWD/ql/data:/ql/data \ -v $PWD/ql/data:/ql/data \
-p 5700:5700 \ -p 5700:5700 \
# 部署路径非必须,以斜杠开头和结尾,比如 /test/
-e QlBaseUrl="/" \
--name qinglong \ --name qinglong \
--hostname qinglong \ --hostname qinglong \
docker.io/whyour/qinglong:latest docker.io/whyour/qinglong:latest
@@ -97,6 +99,8 @@ systemctl restart docker
docker run -dit \ docker run -dit \
-v $PWD/ql/data:/ql/data \ -v $PWD/ql/data:/ql/data \
-p 5700:5700 \ -p 5700:5700 \
# 部署路径非必须,以斜杠开头和结尾,比如 /test/
-e QlBaseUrl="/" \
--name qinglong \ --name qinglong \
--hostname qinglong \ --hostname qinglong \
--restart unless-stopped \ --restart unless-stopped \
@@ -197,7 +201,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);
+5
View File
@@ -92,6 +92,11 @@ export default {
'/api/system', '/api/system',
'/api/user/init', '/api/user/init',
'/api/user/notification/init', '/api/user/notification/init',
'/open/user/login',
'/open/user/two-factor/login',
'/open/system',
'/open/user/init',
'/open/user/notification/init',
], ],
versionFile, versionFile,
lastVersionFile, lastVersionFile,
+20
View File
@@ -0,0 +1,20 @@
import { Request, Response } from 'express';
import { pick } from 'lodash';
let pickedEnv: Record<string, string>;
function getPickedEnv() {
if (pickedEnv) return pickedEnv;
const picked = pick(process.env, ['QlBaseUrl']);
pickedEnv = picked as Record<string, string>;
return picked;
}
export function serveEnv(_req: Request, res: Response) {
res.type('.js');
res.send(
Object.entries(getPickedEnv())
.map(([k, v]) => `window.__ENV__${k}=${JSON.stringify(v)};`)
.join('\n'),
);
}
+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,
+13 -10
View File
@@ -14,22 +14,25 @@ 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';
import { serveEnv } from '../config/serverEnv';
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.get(`${config.api.prefix}/env.js`, serveEnv);
app.use(`${config.api.prefix}/static`, express.static(config.uploadPath)); app.use(`${config.api.prefix}/static`, express.static(config.uploadPath));
app.use((req, res, next) => { app.use(
if (req.path.startsWith('/api') || req.path.startsWith('/open')) { '/api/public',
next(); createProxyMiddleware({
} else { target: `http://localhost:${config.publicPort}/api`,
return handler(req, res, { changeOrigin: true,
public: 'static/dist', pathRewrite: { '/api/public': '' },
rewrites: [{ source: '**', destination: '/index.html' }], }),
}); );
}
});
app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
+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);
+5 -2
View File
@@ -17,10 +17,13 @@ const check = async (
if (res.includes('200')) { if (res.includes('200')) {
return callback(null, { status: 1 }); return callback(null, { status: 1 });
} }
const errLog = await promiseExec( const panelErrLog = await promiseExec(
`tail -n 300 ~/.pm2/logs/panel-error.log`, `tail -n 300 ~/.pm2/logs/panel-error.log`,
); );
return callback(new Error(errLog)); const scheduleErrLog = await promiseExec(
`tail -n 300 ~/.pm2/logs/schedule-error.log`,
);
return callback(new Error(`${scheduleErrLog}\n${panelErrLog}`));
default: default:
return callback(null, { status: 1 }); return callback(null, { status: 1 });
+5 -1
View File
@@ -13,8 +13,12 @@ server.addService(CronService, { addCron, delCron });
server.bindAsync( server.bindAsync(
`localhost:${config.cronPort}`, `localhost:${config.cronPort}`,
ServerCredentials.createInsecure(), ServerCredentials.createInsecure(),
() => { (err, port) => {
if (err) {
throw err;
}
server.start(); server.start();
Logger.debug(`✌️ 定时服务启动成功!`); Logger.debug(`✌️ 定时服务启动成功!`);
process.send?.('ready');
}, },
); );
+83 -19
View File
@@ -93,7 +93,11 @@ export default class NotificationService {
}, },
}) })
.json(); .json();
return typeof res.id === 'number'; if (typeof res.id === 'number') {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -109,7 +113,11 @@ export default class NotificationService {
headers: { Authorization: 'Bearer ' + goCqHttpBotToken }, headers: { Authorization: 'Bearer ' + goCqHttpBotToken },
}) })
.json(); .json();
return res.retcode === 0; if (res.retcode === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -128,7 +136,11 @@ export default class NotificationService {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}) })
.json(); .json();
return res.errno === 0 || res.data.errno === 0; if (res.errno === 0 || res.data.errno === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -147,9 +159,14 @@ export default class NotificationService {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}) })
.json(); .json();
return ( if (
res.content.result.length !== undefined && res.content.result.length > 0 res.content.result.length !== undefined &&
); res.content.result.length > 0
) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -166,7 +183,11 @@ export default class NotificationService {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}) })
.json(); .json();
return res.success; if (res.success) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -190,7 +211,11 @@ export default class NotificationService {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
}) })
.json(); .json();
return res.code === 200; if (res.code === 200) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -234,7 +259,11 @@ export default class NotificationService {
agent, agent,
}) })
.json(); .json();
return !!res.ok; if (res.ok) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -263,7 +292,11 @@ export default class NotificationService {
}, },
}) })
.json(); .json();
return res.errcode === 0; if (res.errcode === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -284,7 +317,11 @@ export default class NotificationService {
}, },
}) })
.json(); .json();
return res.errcode === 0; if (res.errcode === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -359,7 +396,11 @@ export default class NotificationService {
) )
.json(); .json();
return res.errcode === 0; if (res.errcode === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -403,8 +444,11 @@ export default class NotificationService {
}, },
}) })
.json(); .json();
if (res.code === 0) {
return res.code === 0; return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -422,7 +466,11 @@ export default class NotificationService {
}) })
.json(); .json();
return res.ret === 0; if (res.ret === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -444,7 +492,11 @@ export default class NotificationService {
}) })
.json(); .json();
return res.code === 200; if (res.code === 200) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -468,7 +520,11 @@ export default class NotificationService {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
}) })
.json(); .json();
return res.StatusCode === 0; if (res.StatusCode === 0) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -495,7 +551,11 @@ export default class NotificationService {
transporter.close(); transporter.close();
return !!info.messageId; if (info.messageId) {
return true;
} else {
throw new Error(JSON.stringify(info));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
@@ -529,7 +589,11 @@ export default class NotificationService {
}; };
try { try {
const res = await got(formatUrl, options); const res = await got(formatUrl, options);
return String(res.statusCode).startsWith('20'); if (String(res.statusCode).startsWith('20')) {
return true;
} else {
throw new Error(JSON.stringify(res));
}
} catch (error: any) { } catch (error: any) {
throw new Error(error.response ? error.response.body : error); throw new Error(error.response ? error.response.body : error);
} }
+3 -1
View File
@@ -60,7 +60,9 @@ export default class SshKeyService {
if (host === 'github.com') { if (host === 'github.com') {
host = `ssh.github.com\n Port 443\n HostkeyAlgorithms +ssh-rsa\n PubkeyAcceptedAlgorithms +ssh-rsa`; host = `ssh.github.com\n Port 443\n HostkeyAlgorithms +ssh-rsa\n PubkeyAcceptedAlgorithms +ssh-rsa`;
} }
const proxyStr = proxy ? ` ProxyCommand nc -v -x ${proxy} %h %p\n` : ''; const proxyStr = proxy
? ` ProxyCommand nc -v -x ${proxy} %h %p 2>/dev/null\n`
: '';
const config = `Host ${alias}\n Hostname ${host}\n IdentityFile ${path.join( const config = `Host ${alias}\n Hostname ${host}\n IdentityFile ${path.join(
this.sshPath, this.sshPath,
alias, alias,
+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"]
+6 -5
View File
@@ -1,14 +1,15 @@
version: '2' version: '2'
services: services:
web: web:
# alpine 基础镜像版本
image: whyour/qinglong:latest image: whyour/qinglong:latest
# debian-slim 基础镜像版本
# image: whyour/qinglong:debian
volumes: volumes:
- ./data:/ql/data - ./data:/ql/data
ports: ports:
- "0.0.0.0:5700:5700" - "0.0.0.0:5700:5700"
environment:
# 部署路径非必须,以斜杠开头和结尾,比如 /test/
QlBaseUrl: '/'
restart: unless-stopped restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-sf", "http://127.0.0.1:5400/api/health", "||", "exit", "1"]
interval: 2m
timeout: 10s
retries: 3
+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
+10 -6
View File
@@ -14,31 +14,33 @@ map $http_upgrade $connection_upgrade {
server { server {
listen 5700; listen 5700;
IPV6_CONFIG IPV6_CONFIG
root /ql/static/dist;
ssl_session_timeout 5m; ssl_session_timeout 5m;
location QL_BASE_URL/api/public/ { location QL_BASE_URLapi/public/ {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
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_URLapi/ {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
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;
} }
location QL_BASE_URL/open/ { location QL_BASE_URLopen/ {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
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;
@@ -49,10 +51,12 @@ server {
gzip_comp_level 6; gzip_comp_level 6;
gzip_buffers 16 8k; gzip_buffers 16 8k;
gzip_http_version 1.0; gzip_http_version 1.0;
QL_ROOT_CONFIG
location QL_BASE_URL/ { location QL_BASE_URL_LOCATION {
QL_ALIAS_CONFIG
index index.html index.htm; index index.html index.htm;
try_files $uri $uri/ QL_BASE_URL/index.html; try_files $uri QL_BASE_URLindex.html;
} }
location ~ .*\.(html)$ { location ~ .*\.(html)$ {
+34
View File
@@ -0,0 +1,34 @@
module.exports = {
apps: [
{
name: 'schedule',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
source_map_support: true,
time: true,
script: 'static/build/schedule/index.js',
},
{
name: 'public',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
source_map_support: true,
time: true,
script: 'static/build/public.js',
},
{
name: 'panel',
max_restarts: 10,
kill_timeout: 15000,
wait_ready: true,
listen_timeout: 10000,
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
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.8 KiB

+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
+1 -1
View File
@@ -193,7 +193,7 @@ run_else() {
local relative_path="${file_param%/*}" local relative_path="${file_param%/*}"
if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
cd ${relative_path} cd ${relative_path}
file_param=${file_param/$relative_path\//} file_param=${file_param/$relative_path\//.\/}
fi fi
shift shift
+44 -63
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
@@ -34,7 +35,6 @@ file_notify_js_sample=$dir_sample/notify.js
file_notify_py_sample=$dir_sample/notify.py file_notify_py_sample=$dir_sample/notify.py
file_notify_py=$dir_scripts/notify.py file_notify_py=$dir_scripts/notify.py
file_notify_js=$dir_scripts/sendNotify.js file_notify_js=$dir_scripts/sendNotify.js
task_error_log_path=$dir_log/task_error.log
nginx_app_conf=$dir_root/docker/front.conf nginx_app_conf=$dir_root/docker/front.conf
nginx_conf=$dir_root/docker/nginx.conf nginx_conf=$dir_root/docker/nginx.conf
dep_notify_py=$dir_dep/notify.py dep_notify_py=$dir_dep/notify.py
@@ -67,7 +67,7 @@ import_config() {
[[ -f $file_config_user ]] && . $file_config_user [[ -f $file_config_user ]] && . $file_config_user
[[ -f $file_env ]] && . $file_env [[ -f $file_env ]] && . $file_env
ql_base_url=${QlBaseUrl:-""} ql_base_url=${QlBaseUrl:-"/"}
command_timeout_time=${CommandTimeoutTime:-""} command_timeout_time=${CommandTimeoutTime:-""}
proxy_url=${ProxyUrl:-""} proxy_url=${ProxyUrl:-""}
file_extensions=${RepoFileExtensions:-"js py"} file_extensions=${RepoFileExtensions:-"js py"}
@@ -98,6 +98,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 +265,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 +272,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 +303,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 +319,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 +355,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 +374,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 +428,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
@@ -492,8 +467,20 @@ patch_version() {
init_nginx() { 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 local location_url="/"
local aliasStr=""
local rootStr=""
if [[ $ql_base_url != "/" ]]; then
location_url="^~${ql_base_url%*/}"
aliasStr="alias ${dir_static}/dist;"
else
rootStr="root ${dir_static}/dist;"
fi
sed -i "s,QL_ALIAS_CONFIG,${aliasStr},g" /etc/nginx/conf.d/front.conf
sed -i "s,QL_ROOT_CONFIG,${rootStr},g" /etc/nginx/conf.d/front.conf
sed -i "s,QL_BASE_URL_LOCATION,${location_url},g" /etc/nginx/conf.d/front.conf
sed -i "s,QL_BASE_URL,${ql_base_url},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
@@ -509,11 +496,6 @@ handle_task_before() {
[[ $is_macos -eq 0 ]] && check_server [[ $is_macos -eq 0 ]] && check_server
if [[ -s $task_error_log_path ]]; then
cat $task_error_log_path
echo -e "加载 config.sh 出错,请手动检查"
fi
. $file_task_before "$@" . $file_task_before "$@"
} }
@@ -536,6 +518,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
+6 -9
View File
@@ -243,10 +243,9 @@ update_qinglong() {
echo -e "使用 ${mirror} 源更新...\n" echo -e "使用 ${mirror} 源更新...\n"
export isFirstStartServer=false export isFirstStartServer=false
local all_branch=$(cd ${dir_root} && git branch -a)
local primary_branch="master" local primary_branch="master"
if [[ "${all_branch}" =~ "${current_branch}" ]]; then if [[ "${QL_BRANCH}" == "develop" ]]; then
primary_branch="${current_branch}" primary_branch="develop"
fi fi
[[ -f $dir_root/package.json ]] && ql_depend_old=$(cat $dir_root/package.json) [[ -f $dir_root/package.json ]] && ql_depend_old=$(cat $dir_root/package.json)
reset_romote_url ${dir_root} "https://${mirror}.com/whyour/qinglong.git" ${primary_branch} reset_romote_url ${dir_root} "https://${mirror}.com/whyour/qinglong.git" ${primary_branch}
@@ -257,6 +256,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"
@@ -454,12 +456,6 @@ main() {
eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd eval echo -e "\#\# 开始执行... $begin_time\\\n" $cmd
fi fi
if [[ -s $task_error_log_path ]]; then
eval cat $task_error_log_path $cmd
eval echo -e "加载 config.sh 出错,请手动检查" $cmd
eval echo $cmd
fi
if [[ "$show_log" == "true" ]] && [[ $ID ]]; then if [[ "$show_log" == "true" ]] && [[ $ID ]]; then
eval echo -e "请移除 -l 参数" $cmd eval echo -e "请移除 -l 参数" $cmd
exit 1 exit 1
@@ -467,6 +463,7 @@ main() {
case $p1 in case $p1 in
update) update)
fix_config
eval update_qinglong "$2" $cmd eval update_qinglong "$2" $cmd
;; ;;
extra) extra)
+16
View File
@@ -0,0 +1,16 @@
const baseUrl = window.__ENV__QlBaseUrl || '/';
export function modifyClientRenderOpts(memo: any) {
return {
...memo,
publicPath: baseUrl,
basename: baseUrl,
};
}
export function modifyContextOpts(memo: any) {
return {
...memo,
basename: baseUrl,
};
}
+8 -2
View File
@@ -332,8 +332,14 @@ select:-webkit-autofill:focus {
} }
.ant-pro-sider-logo { .ant-pro-sider-logo {
h1 { .title {
margin-left: 5px !important; height: 32px;
margin: 0 5px;
font-weight: 600;
font-size: 16px;
line-height: 32px;
vertical-align: middle;
animation: pro-layout-title-hide 0.3s;
} }
img { img {
+23 -10
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') {
@@ -267,11 +280,10 @@ export default function () {
selectedKeys={[location.pathname]} selectedKeys={[location.pathname]}
loading={loading} loading={loading}
ErrorBoundary={Sentry.ErrorBoundary} ErrorBoundary={Sentry.ErrorBoundary}
logo={<Image preview={false} src="https://qn.whyour.cn/logo.png" />} logo={
// @ts-ignore
title={
<> <>
<span style={{ fontSize: 16, marginRight: 5 }}></span> <Image preview={false} src="https://qn.whyour.cn/logo.png" />
<span className="title"></span>
<a <a
href={systemInfo?.changeLogLink} href={systemInfo?.changeLogLink}
target="_blank" target="_blank"
@@ -300,6 +312,7 @@ export default function () {
</a> </a>
</> </>
} }
title={false}
menuItemRender={(menuItemProps: any, defaultDom: any) => { menuItemRender={(menuItemProps: any, defaultDom: any) => {
if ( if (
menuItemProps.isUrl || menuItemProps.isUrl ||
+2 -2
View File
@@ -253,9 +253,9 @@ const ViewCreateModal = ({
name={[name, 'value']} name={[name, 'value']}
rules={[{ required: true, message: '请输入内容' }]} rules={[{ required: true, message: '请输入内容' }]}
> >
{EOperation[filtersValue[name]['operation']] === {EOperation[filtersValue?.[name]['operation']] ===
'select' ? ( 'select' ? (
statusElement(filtersValue[name]['property']) statusElement(filtersValue?.[name]['property'])
) : ( ) : (
<Input placeholder="请输入内容" /> <Input placeholder="请输入内容" />
)} )}
+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;
} }
} }
} }
+28 -6
View File
@@ -17,9 +17,9 @@ 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, data }) => {
if (status === 1) { if (data?.status === 1) {
return reloadUser(); return;
} }
if (retryTimes.current > 3) { if (retryTimes.current > 3) {
setData(error?.details); setData(error?.details);
@@ -27,7 +27,6 @@ const Error = () => {
} }
retryTimes.current += 1; retryTimes.current += 1;
setTimeout(() => { setTimeout(() => {
reloadUser();
getLog(false); getLog(false);
}, 3000); }, 3000);
}) })
@@ -53,10 +52,33 @@ 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></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>
<div>
3. pm2 logs
<Typography.Link href="https://github.com/whyour/qinglong/issues/new?assignees=&labels=&template=bug_report.yml">
issue
</Typography.Link>
</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();
}} }}
> >
+4 -2
View File
@@ -1,6 +1,8 @@
const baseUrl = window.__ENV__QlBaseUrl || '/';
export default { export default {
siteName: '青龙', siteName: '青龙',
apiPrefix: '/api/', apiPrefix: `${baseUrl}api/`,
authKey: 'token', authKey: 'token',
/* Layout configuration, specify which layout to use for route. */ /* Layout configuration, specify which layout to use for route. */
@@ -91,7 +93,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: '自定义通知' },
+10 -3
View File
@@ -4,7 +4,7 @@ import config from './config';
import { history } from '@umijs/max'; import { history } from '@umijs/max';
message.config({ message.config({
duration: 1.5, duration: 2,
}); });
const time = Date.now(); const time = Date.now();
@@ -23,7 +23,10 @@ const errorHandler = function (error: any) {
history.push('/login'); history.push('/login');
} }
} else { } else {
message.error(msg); message.error({
content: msg,
style: { maxWidth: 500, margin: '0 auto' },
});
} }
} else { } else {
console.log(error.message); console.log(error.message);
@@ -66,7 +69,11 @@ _request.interceptors.response.use(async (response) => {
const res = await response.clone().json(); const res = await response.clone().json();
if (res.code !== 200) { if (res.code !== 200) {
const msg = res.message || res.data; const msg = res.message || res.data;
msg && message.error(msg); msg &&
message.error({
content: msg,
style: { maxWidth: 500, margin: '0 auto' },
});
} }
return res; return res;
} }
+4
View File
@@ -10,3 +10,7 @@ declare module '*.svg' {
} }
declare module 'pstree.remy'; declare module 'pstree.remy';
interface Window {
__ENV__QlBaseUrl: string;
}
+5 -10
View File
@@ -1,11 +1,6 @@
version: 2.15.13 version: 2.15.15
changeLogLink: https://t.me/jiao_long/373 changeLogLink: https://t.me/jiao_long/376
changeLog: | changeLog: |
1. 增加运行、停止指定命令接口 system/command-run、system/command-stop 1. 支持非跟目录部署,启动时增加参数 -e QlBaseUrl="/test/",参数前后需要斜杠
2. 增加容器健康检查 2. 修复 task 执行可执行文件路径,感谢 https://github.com/jzksnsjswkw
3. 移除执行任务默认超时时间 3. 其他 bug 修复
4. 修改 task 命令生成日志逻辑和关联任务查询
5. 修复更新任务、环境变量、依赖、订阅状态丢失
6. 修改系统通知错误提示
7. 修复系统通知 gotify 配置
8. 其他 bug 修复