Compare commits

..

51 Commits

Author SHA1 Message Date
whyour 0caf53b147 修复删除环境变量无效 2022-02-17 11:14:43 +08:00
whyour 4d1354d5f4 更新版本 v2.11.3 2022-02-16 23:09:33 +08:00
whyour fc4562eca2 修改check命令,安装依赖使用代理
修改apk源地址
2022-02-16 23:06:12 +08:00
whyour 5b048a5510 修改logo地址 2022-02-16 22:56:54 +08:00
whyour d23049b2fe 移除esbuild 2022-02-11 13:52:12 +08:00
whyour caac5607b9 环境变量增加更新时间排序 2022-02-11 13:41:48 +08:00
Ghost Lee fb2e69b12d 修复goCqHttpBotToken中存在&字符导致认证失败的问题 (#993)
修改goCqHttpBot鉴权方式,调整为使用Header模式
2022-02-08 18:45:24 +08:00
Ukenn fc1838ff39 bark支持自定义推送图标 (#1146)
* bark添加自定义推送图标

* 脚本bark通知添加自定义推送图标
2022-02-08 18:21:37 +08:00
whyour 25689cf40f 修复定时任务排序 2022-02-01 22:33:18 +08:00
whyour c746a3d215 修复.env 2022-01-30 20:22:00 +08:00
whyour 548d924fa0 更新版本 v2.11.2 2022-01-30 18:25:18 +08:00
whyour 18a6be3ecd 修改placeholder 2022-01-30 17:39:27 +08:00
whyour afef0cb72f 更新dockerfile 2022-01-30 14:59:41 +08:00
whyour 1221b47eca 修改dockerfile 2022-01-30 14:03:57 +08:00
whyour a356eeb5da 修改base context 2022-01-30 12:37:20 +08:00
whyour 25bb0972b9 修改dockerfile 2022-01-30 12:31:49 +08:00
whyour 0914075cf6 修改定时任务唯一键 2022-01-30 10:11:47 +08:00
whyour 74b258e1b6 修改定时任务排序 2022-01-30 09:59:33 +08:00
whyour 27eb0a21e4 修改dockerfile 2022-01-29 14:23:19 +08:00
whyour d77c6d50d8 修改dockerfile 2022-01-29 12:53:21 +08:00
whyour 6a64c18a7b 修改dockerfile 2022-01-29 12:15:25 +08:00
whyour b97ff714ae 修复dockerfile 2022-01-29 11:45:06 +08:00
whyour 1f23eea805 更新dockerfile 2022-01-29 11:20:43 +08:00
whyour cb0cb7df21 修改dockerfile 2022-01-29 00:09:37 +08:00
whyour f3e8628664 移除prepare scripts 2022-01-28 23:46:17 +08:00
whyour 4758471f9f 修改sentry依赖 2022-01-28 23:19:39 +08:00
whyour c10bd5a549 修改sentry cli版本 2022-01-28 23:14:12 +08:00
whyour d53faf96eb 修改sentry依赖 2022-01-28 22:47:46 +08:00
whyour 2307b8ed1a 增加esbuild 2022-01-28 21:53:05 +08:00
whyour 711af8d4a5 修改dockfile 2022-01-28 20:54:00 +08:00
whyour 458c9c456b 修改dockerfile 2022-01-28 20:27:38 +08:00
whyour 29069df48c 修复dockerfile 2022-01-28 19:33:01 +08:00
whyour 300e84ae2c 修改dockerfile 2022-01-28 19:23:57 +08:00
whyour bf11768b92 修改base镜像 2022-01-27 23:09:29 +08:00
whyour 91bd1d9263 测试青龙基础镜像 2022-01-27 23:06:35 +08:00
whyour ac40f9ff12 测试最新alpine 2022-01-27 22:21:34 +08:00
whyour f930837220 修复获取克隆仓库状态 2022-01-27 22:15:45 +08:00
whyour 1d5e3acc93 去除sql执行日志 2022-01-27 00:56:37 +08:00
whyour 2aec1f9846 修改表唯一约束 2022-01-27 00:49:44 +08:00
whyour b56a681b98 python默认实时输出 2022-01-25 23:03:56 +08:00
whyour 2713419b32 修复修改任务状态本地免认证 2022-01-25 22:52:27 +08:00
whyour 764a3492cc 添加关于页 2022-01-24 23:17:12 +08:00
whyour 29fc471ac4 修复日志加载状态 2022-01-24 22:50:01 +08:00
whyour 54a597aad3 修改logo地址 2022-01-23 16:55:11 +08:00
whyour 778961d0b6 修改node基础镜像版本 2022-01-22 22:09:16 +08:00
whyour 14b01ac761 测试基础镜像node版本 2022-01-22 21:57:02 +08:00
whyour f87848c05b 修改darkreader版本 2022-01-22 21:09:01 +08:00
whyour 153e5a806d 增加sqlite连接池 2022-01-22 20:53:22 +08:00
whyour ee1b8a906f 移除cdn依赖 2022-01-22 20:25:22 +08:00
whyour 734685d45a 定时任务默认改为创建时间倒序 2022-01-21 22:38:13 +08:00
whyour e3b540c212 修复更新系统通知 2022-01-21 22:27:50 +08:00
42 changed files with 8650 additions and 8681 deletions
+2 -1
View File
@@ -2,7 +2,8 @@ MONGODB_URI='mongodb://'
YIYAN_MONGODB_URI=''
CRON_PORT=5500
PORT=5600
BACK_PORT=5600
PORT=5700
LOG_LEVEL='debug'
+59
View File
@@ -0,0 +1,59 @@
name: Build QL BASE
on:
schedule:
- cron: '00 18 * * *' # GMT 14:00 => 北京时间 2:00
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '14'
- name: Set time zone
uses: szenius/set-timezone@v1.0
with:
timezoneLinux: "Asia/Shanghai"
timezoneMacos: "Asia/Shanghai"
timezoneWindows: "China Standard Time"
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GHCR
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
build-args: |
MAINTAINER=${{ github.repository_owner }}
QL_BRANCH=${{ github.ref_name }}
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x
context: .
file: docker/base/Dockerfile
push: true
tags: whyour/ql:base
+2 -2
View File
@@ -28,8 +28,8 @@ jobs:
- name: build front and back
run: |
yarn install
yarn build
yarn build-back
yarn build:front
yarn build:back
- name: copy to static repo
env:
-9
View File
@@ -32,18 +32,9 @@ export default defineConfig({
externals: {
react: 'window.React',
'react-dom': 'window.ReactDOM',
darkreader: 'window.DarkReader',
codemirror: 'window.CodeMirror',
'sockjs-client': 'window.SockJS',
},
scripts: [
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
'https://unpkg.zhimg.com/darkreader@4.9.40/darkreader.js',
'https://unpkg.zhimg.com/codemirror@5/lib/codemirror.js',
'https://unpkg.zhimg.com/codemirror@5/mode/shell/shell.js',
'https://unpkg.zhimg.com/codemirror@5/mode/python/python.js',
'https://unpkg.zhimg.com/codemirror@5/mode/javascript/javascript.js',
'https://unpkg.zhimg.com/sockjs-client@1/dist/sockjs.min.js',
],
});
+1 -2
View File
@@ -153,11 +153,10 @@ $ git clone git@github.com:whyour/qinglong.git
$ cd qinglong
$ cp .env.example .env
$ yarn install
$ yarn start-back
$ yarn start
```
打开你的浏览器,访问 http://127.0.0.1:5601
打开你的浏览器,访问 http://127.0.0.1:5700
## 交流
+1 -1
View File
@@ -37,7 +37,7 @@ if (envFound.error) {
}
export default {
port: parseInt(process.env.PORT as string, 10),
port: parseInt(process.env.BACK_PORT as string, 10),
cronPort: parseInt(process.env.CRON_PORT as string, 10),
secret: process.env.SECRET || createRandomString(16, 32),
logs: {
+10 -4
View File
@@ -49,12 +49,18 @@ export enum CrontabStatus {
interface CronInstance extends Model<Crontab, Crontab>, Crontab {}
export const CrontabModel = sequelize.define<CronInstance>('Crontab', {
name: DataTypes.STRING,
command: {
unique: 'command',
name: {
unique: 'compositeIndex',
type: DataTypes.STRING,
},
command: {
unique: 'compositeIndex',
type: DataTypes.STRING,
},
schedule: {
unique: 'compositeIndex',
type: DataTypes.STRING,
},
schedule: DataTypes.STRING,
timestamp: DataTypes.STRING,
saved: DataTypes.BOOLEAN,
status: DataTypes.NUMBER,
+2 -2
View File
@@ -30,10 +30,10 @@ export const initEnvPosition = 9999999999;
interface EnvInstance extends Model<Env, Env>, Env {}
export const EnvModel = sequelize.define<EnvInstance>('Env', {
value: DataTypes.STRING,
value: { type: DataTypes.STRING, unique: 'compositeIndex' },
timestamp: DataTypes.STRING,
status: DataTypes.NUMBER,
position: DataTypes.NUMBER,
name: DataTypes.STRING,
name: { type: DataTypes.STRING, unique: 'compositeIndex' },
remarks: DataTypes.STRING,
});
+7
View File
@@ -5,4 +5,11 @@ export const sequelize = new Sequelize({
dialect: 'sqlite',
storage: `${config.dbPath}database.sqlite`,
logging: false,
pool: {
max: 6,
min: 0,
idle: 30000,
},
});
export type ResponseType<T> = { code: number; data?: T; message?: string };
+2
View File
@@ -34,6 +34,8 @@ export class ServerChanNotification extends NotificationBaseInfo {
export class BarkNotification extends NotificationBaseInfo {
public barkPush = '';
public barkIcon =
'https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png';
public barkSound = '';
public barkGroup = 'qinglong';
}
+1 -1
View File
@@ -35,7 +35,7 @@ export enum CrontabStatus {
interface AppInstance extends Model<App, App>, App {}
export const AppModel = sequelize.define<AppInstance>('App', {
name: DataTypes.STRING,
name: { type: DataTypes.STRING, unique: 'name' },
scopes: DataTypes.JSON,
client_id: DataTypes.STRING,
client_secret: DataTypes.STRING,
+11 -1
View File
@@ -11,7 +11,17 @@ import { sequelize } from '../data';
export default async () => {
try {
await sequelize.sync({ alter: true });
await sequelize.sync();
await new Promise((resolve) => setTimeout(() => resolve(null), 5000));
// try {
// const queryInterface = sequelize.getQueryInterface();
// await queryInterface.addIndex('Crontabs', ['command'], { unique: true });
// await queryInterface.addIndex('Envs', ['name', 'value'], { unique: true });
// await queryInterface.addIndex('Apps', ['name'], { unique: true });
// } catch (error) {
// }
const crondbExist = await fileExist(config.cronDbFile);
const dependenceDbExist = await fileExist(config.dependenceDbFile);
+5 -4
View File
@@ -68,18 +68,19 @@ export default ({ app }: { app: Application }) => {
}
}
const originPath = `${req.baseUrl}${req.path === '/' ? '' : req.path}`;
if (
!headerToken &&
req.path &&
config.apiWhiteList.includes(req.path) &&
req.path !== '/api/crons/status'
originPath &&
config.apiWhiteList.includes(originPath) &&
originPath !== '/api/crons/status'
) {
return next();
}
const remoteAddress = req.socket.remoteAddress;
if (
remoteAddress === '::ffff:127.0.0.1' &&
req.path === '/api/crons/status'
originPath === '/api/crons/status'
) {
return next();
}
+4 -9
View File
@@ -5,7 +5,7 @@ import { Crontab, CrontabModel, CrontabStatus } from '../data/cron';
import { exec, execSync, spawn } from 'child_process';
import fs from 'fs';
import cron_parser from 'cron-parser';
import { getFileContentByName, concurrentRun } from '../config/util';
import { getFileContentByName, concurrentRun, fileExist } from '../config/util';
import { promises, existsSync } from 'fs';
import { promisify } from 'util';
import { Op } from 'sequelize';
@@ -31,12 +31,6 @@ export default class CronService {
}
public async insert(payload: Crontab): Promise<Crontab> {
const cron = await CrontabModel.findOne({
where: { command: payload.command },
});
if (cron) {
return cron;
}
return await CrontabModel.create(payload, { returning: true });
}
@@ -165,7 +159,7 @@ export default class CronService {
try {
const result = await CrontabModel.findAll({
where: query,
order: [['updatedAt', 'DESC']],
order: [['createdAt', 'DESC']],
});
return result as any;
} catch (error) {
@@ -200,7 +194,8 @@ export default class CronService {
}
}
const err = await this.killTask(doc.command);
if (doc.log_path) {
const logFileExist = await fileExist(doc.log_path);
if (doc.log_path && logFileExist) {
const str = err ? `\n${err}` : '';
fs.appendFileSync(
`${doc.log_path}`,
+7 -2
View File
@@ -28,8 +28,12 @@ export default class EnvService {
}
public async insert(payloads: Env[]): Promise<Env[]> {
const docs = await EnvModel.bulkCreate(payloads);
return docs;
const result = [];
for (const env of payloads) {
const doc = await EnvModel.create(env, { returning: true });
result.push(doc);
}
return result;
}
public async update(payload: Env): Promise<Env> {
@@ -45,6 +49,7 @@ export default class EnvService {
public async remove(ids: string[]) {
await EnvModel.destroy({ where: { id: ids } });
await this.set_envs();
}
public async move(
+4 -3
View File
@@ -91,11 +91,12 @@ export default class NotificationService {
const { goCqHttpBotQq, goCqHttpBotToken, goCqHttpBotUrl } = this.params;
const res: any = await got
.post(
`${goCqHttpBotUrl}?access_token=${goCqHttpBotToken}&${goCqHttpBotQq}`,
`${goCqHttpBotUrl}?${goCqHttpBotQq}`,
{
timeout: this.timeout,
retry: 0,
json: { message: `${this.title}\n${this.content}` },
headers: { 'Authorization': 'Bearer '+goCqHttpBotToken },
},
)
.json();
@@ -119,7 +120,7 @@ export default class NotificationService {
}
private async bark() {
let { barkPush, barkSound, barkGroup } = this.params;
let { barkPush, barkIcon, barkSound, barkGroup } = this.params;
if (!barkPush.startsWith('http') && !barkPush.startsWith('https')) {
barkPush = `https://api.day.app/${barkPush}`;
}
@@ -127,7 +128,7 @@ export default class NotificationService {
this.title,
)}/${encodeURIComponent(
this.content,
)}?sound=${barkSound}&group=${barkGroup}`;
)}?icon=${barkIcon}?sound=${barkSound}&group=${barkGroup}`;
const res: any = await got
.get(url, {
timeout: this.timeout,
+2 -2
View File
@@ -24,8 +24,8 @@ export default class OpenService {
return { ...doc, tokens: [] };
}
public async insert(payloads: App): Promise<App> {
const doc = await AppModel.create(payloads, { returning: true });
public async insert(payload: App): Promise<App> {
const doc = await AppModel.create(payload, { returning: true });
return doc.get({ plain: true }) as App;
}
+2 -2
View File
@@ -86,9 +86,9 @@ export default class SystemService {
let lastLog = '';
try {
const result = await Promise.race([
got.get(config.lastVersionFile, { timeout: 1000, retry: 0 }),
got.get(config.lastVersionFile, { timeout: 6000, retry: 0 }),
got.get(`https://ghproxy.com/${config.lastVersionFile}`, {
timeout: 5000,
timeout: 6000,
retry: 0,
}),
]);
+10 -2
View File
@@ -313,8 +313,16 @@ export default class UserService {
}
private async updateAuthDb(payload: AuthInfo): Promise<any> {
await AuthModel.upsert({ ...payload });
const doc = await this.getDb({ type: payload.type });
let doc = await AuthModel.findOne({ type: payload.type });
if (doc) {
const updateResult = await AuthModel.update(payload, {
where: { id: doc.id },
returning: true,
});
doc = updateResult[1][0];
} else {
doc = await AuthModel.create(payload, { returning: true });
}
return doc;
}
+8 -33
View File
@@ -1,53 +1,28 @@
FROM node:lts-alpine3.12
FROM whyour/ql:base
ARG QL_MAINTAINER="whyour"
LABEL maintainer="${QL_MAINTAINER}"
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
ARG QL_BRANCH=master
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
LANG=zh_CN.UTF-8 \
SHELL=/bin/bash \
PS1="\u@\h:\w \$ " \
QL_DIR=/ql \
QL_BRANCH=${QL_BRANCH}
WORKDIR ${QL_DIR}
RUN set -x \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& apk update -f \
&& apk upgrade \
&& apk --no-cache add -f bash \
coreutils \
moreutils \
git \
curl \
wget \
tzdata \
perl \
openssl \
nginx \
python3 \
jq \
openssh \
py3-pip \
python2 \
g++ \
make \
&& rm -rf /var/cache/apk/* \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& touch ~/.bashrc \
&& git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
&& git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \
RUN git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
&& cd ${QL_DIR} \
&& cp -f .env.example .env \
&& chmod 777 ${QL_DIR}/shell/*.sh \
&& chmod 777 ${QL_DIR}/docker/*.sh \
&& npm install -g pnpm \
&& pnpm install -g pm2 \
&& pnpm install -g ts-node typescript tslib \
&& rm -rf /root/.npm \
&& cp -rf /node_modules ./ \
&& rm -rf /node_modules \
&& pnpm install --prod \
&& rm -rf /root/.pnpm-store \
&& rm -rf /root/.cache \
&& git clone -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
&& cp -rf /static/* ${QL_DIR} \
&& rm -rf /static
+49
View File
@@ -0,0 +1,49 @@
FROM node:alpine
ARG QL_MAINTAINER="whyour"
LABEL maintainer="${QL_MAINTAINER}"
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
LANG=zh_CN.UTF-8 \
SHELL=/bin/bash \
PS1="\u@\h:\w \$ "
COPY package.json /
RUN set -x \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update -f \
&& apk upgrade \
&& apk --no-cache add -f bash \
coreutils \
moreutils \
git \
curl \
wget \
tzdata \
perl \
openssl \
nginx \
python3 \
jq \
openssh \
py3-pip \
python2 \
g++ \
make \
&& rm -rf /var/cache/apk/* \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& touch ~/.bashrc \
&& git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \
&& npm install -g pnpm \
&& pnpm install -g pm2 \
&& pnpm install -g ts-node typescript tslib \
&& cd / && pnpm install --prod \
&& apk --purge del python2 g++ make \
&& rm -rf /root/.npm \
&& rm -rf /root/.pnpm-store \
&& rm -rf /root/.cache \
&& rm -f /package.json
CMD [ "node" ]
+13 -13
View File
@@ -1,14 +1,15 @@
{
"private": true,
"scripts": {
"start": "umi dev",
"build": "umi build",
"build-back": "tsc -p tsconfig.back.json",
"start-back": "nodemon",
"panel": "npm run build-back && node build/app.js",
"schedule": "npm run build-back && node build/schedule.js",
"prepare": "umi generate tmp",
"start": "concurrently -n w: npm:start:*",
"start:front": "umi dev",
"start:back": "nodemon",
"build:front": "umi build",
"build:back": "tsc -p tsconfig.back.json",
"panel": "npm run build:back && node build/app.js",
"schedule": "npm run build:back && node build/schedule.js",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"prepare": "umi generate tmp",
"test": "umi-test",
"test:coverage": "umi-test --coverage"
},
@@ -25,8 +26,8 @@
},
"dependencies": {
"@otplib/preset-default": "^12.0.1",
"@sentry/node": "^6.16.1",
"@sentry/tracing": "^6.16.1",
"@sentry/node": "^6.17.2",
"@sentry/tracing": "^6.17.2",
"body-parser": "^1.19.0",
"celebrate": "^13.0.3",
"chokidar": "^3.5.2",
@@ -60,8 +61,7 @@
"@ant-design/icons": "^4.6.2",
"@ant-design/pro-layout": "^6.26.0",
"@monaco-editor/react": "^4.3.1",
"@sentry/react": "^6.16.1",
"@sentry/webpack-plugin": "^1.18.3",
"@sentry/react": "^6.17.2",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.8",
"@types/express-jwt": "^6.0.1",
@@ -84,9 +84,9 @@
"antd": "^4.17.0-alpha.6",
"codemirror": "^5.62.2",
"compression-webpack-plugin": "6.1.1",
"darkreader": "^4.9.27",
"concurrently": "^7.0.0",
"darkreader": "4.9.40",
"lint-staged": "^10.0.7",
"@mapbox/node-pre-gyp": "1.0.8",
"nodemon": "^2.0.4",
"prettier": "^2.2.0",
"qrcode.react": "^1.0.1",
+2
View File
@@ -50,6 +50,8 @@ export PUSH_KEY=""
## 2. BARK
## 下方填写app提供的设备码,例如:https://api.day.app/123 那么此处的设备码就是123
export BARK_PUSH=""
## 下方填写推送图标设置,自定义推送图标(需iOS15或以上)
export BARK_ICON="https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png"
## 下方填写推送声音设置,例如choo,具体值请在bark-推送铃声-查看所有铃声
export BARK_SOUND=""
## 下方填写推送消息分组,默认为"QingLong"
+22 -12
View File
@@ -25,9 +25,9 @@ let GOTIFY_PRIORITY = 0;
//gobot_token 填写在go-cqhttp文件设置的访问密钥
//gobot_qq 填写推送到个人QQ或者QQ群号
//go-cqhttp相关API https://docs.go-cqhttp.org/api
let GOBOT_URL = ''; // 推送到个人QQ: http://127.0.0.1/send_private_msg 群:http://127.0.0.1/send_group_msg
let GOBOT_URL = ''; // 推送到个人QQ: http://127.0.0.1/send_private_msg 群:http://127.0.0.1/send_group_msg
let GOBOT_TOKEN = ''; //访问密钥
let GOBOT_QQ = ''; // 如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群
let GOBOT_QQ = ''; // 如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群
// =======================================微信server酱通知设置区域===========================================
//此处填你申请的SCKEY.
@@ -37,6 +37,9 @@ let SCKEY = '';
// =======================================Bark App通知设置区域===========================================
//此处填你BarkAPP的信息(IP/设备码,例如:https://api.day.app/XXXXXXXX)
let BARK_PUSH = '';
//BARK app推送图标,自定义推送图标(需iOS15或以上)
let BARK_ICON =
'https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png';
//BARK app推送铃声,铃声列表去APP查看复制填写
let BARK_SOUND = '';
//BARK app推送消息的分组, 默认为"QingLong"
@@ -133,6 +136,9 @@ if (process.env.BARK_PUSH) {
} else {
BARK_PUSH = `https://api.day.app/${process.env.BARK_PUSH}`;
}
if (process.env.BARK_ICON) {
BARK_ICON = process.env.BARK_ICON;
}
if (process.env.BARK_SOUND) {
BARK_SOUND = process.env.BARK_SOUND;
}
@@ -216,8 +222,8 @@ async function sendNotify(
qywxBotNotify(text, desp), //企业微信机器人
qywxamNotify(text, desp), //企业微信应用消息推送
iGotNotify(text, desp, params), //iGot
gobotNotify(text, desp),//go-cqhttp
gotifyNotify(text, desp),//gotify
gobotNotify(text, desp), //go-cqhttp
gotifyNotify(text, desp), //gotify
]);
}
@@ -226,10 +232,12 @@ function gotifyNotify(text, desp) {
if (GOTIFY_URL && GOTIFY_TOKEN) {
const options = {
url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(desp)}&priority=${GOTIFY_PRIORITY}`,
body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(
desp,
)}&priority=${GOTIFY_PRIORITY}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
},
};
$.post(options, (err, resp, data) => {
try {
@@ -261,7 +269,7 @@ function gobotNotify(text, desp, time = 2100) {
if (GOBOT_URL) {
const options = {
url: `${GOBOT_URL}?access_token=${GOBOT_TOKEN}&${GOBOT_QQ}`,
json: {message:`${text}\n${desp}`},
json: { message: `${text}\n${desp}` },
headers: {
'Content-Type': 'application/json',
},
@@ -429,7 +437,9 @@ function BarkNotify(text, desp, params = {}) {
const options = {
url: `${BARK_PUSH}/${encodeURIComponent(text)}/${encodeURIComponent(
desp,
)}?sound=${BARK_SOUND}&group=${BARK_GROUP}&${querystring.stringify(params)}`,
)}?icon=${BARK_ICON}?sound=${BARK_SOUND}&group=${BARK_GROUP}&${querystring.stringify(
params,
)}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
@@ -726,8 +736,8 @@ function qywxamNotify(text, desp) {
if (err) {
console.log(
'成员ID:' +
ChangeUserId(desp) +
'企业微信应用消息发送通知消息失败!!\n',
ChangeUserId(desp) +
'企业微信应用消息发送通知消息失败!!\n',
);
console.log(err);
} else {
@@ -735,8 +745,8 @@ function qywxamNotify(text, desp) {
if (data.errcode === 0) {
console.log(
'成员ID:' +
ChangeUserId(desp) +
'企业微信应用消息发送通知消息成功🎉。\n',
ChangeUserId(desp) +
'企业微信应用消息发送通知消息成功🎉。\n',
);
} else {
console.log(`${data.errmsg}\n`);
+2
View File
@@ -35,6 +35,7 @@ push_config = {
'BARK_ARCHIVE': '', # bark 推送是否存档
'BARK_GROUP': '', # bark 推送分组
'BARK_SOUND': '', # bark 推送声音
'BARK_ICON': '', # bark 推送图标
'CONSOLE': True, # 控制台输出
@@ -104,6 +105,7 @@ def bark(title: str, content: str) -> None:
"BARK_ARCHIVE": "isArchive",
"BARK_GROUP": "group",
"BARK_SOUND": "sound",
"BARK_ICON": "icon",
}
params = ""
for pair in filter(
+2 -4
View File
@@ -10,13 +10,11 @@ reset_env() {
echo -e "---> 配置文件检测完成\n"
echo -e "---> 2. 开始安装青龙依赖\n"
rm -rf $dir_root/node_modules
npm_install_2 $dir_root
echo -e "---> 青龙依赖安装完成\n"
echo -e "---> 3. 开始安装脚本依赖\n"
cp -f $dir_sample/package.json $dir_scripts/package.json
rm -rf $dir_scripts/node_modules
npm_install_2 $dir_scripts
echo -e "---> 脚本依赖安装完成\n"
}
@@ -50,8 +48,8 @@ pm2_log() {
echo -e "---> pm2日志"
local panelOut="/root/.pm2/logs/panel-out.log"
local panelError="/root/.pm2/logs/panel-error.log"
tail -n 20 "$panelOut"
tail -n 20 "$panelError"
tail -n 100 "$panelOut"
tail -n 100 "$panelError"
}
check_nginx() {
+5 -2
View File
@@ -60,6 +60,7 @@ original_name=(
init_env() {
export NODE_PATH=/usr/local/bin:/usr/local/pnpm-global/5/node_modules:/usr/local/lib/node_modules
export PYTHONUNBUFFERED=1
}
import_config() {
@@ -69,6 +70,7 @@ import_config() {
command_timeout_time=${CommandTimeoutTime:-"1h"}
proxy_url=${ProxyUrl:-""}
file_extensions=${RepoFileExtensions:-"js py"}
current_branch=${QL_BRANCH}
if [[ -n "${DefaultCronRule}" ]]; then
default_cron="${DefaultCronRule}"
@@ -240,6 +242,7 @@ fix_config() {
}
npm_install_sub() {
set_proxy
if [ $is_termux -eq 1 ]; then
npm install --production --no-bin-links --registry=https://registry.npm.taobao.org || npm install --production --no-bin-links
elif ! type pnpm &>/dev/null; then
@@ -247,6 +250,7 @@ npm_install_sub() {
else
pnpm install --production --registry=https://registry.npm.taobao.org || pnpm install --production
fi
unset_proxy
}
npm_install_1() {
@@ -302,9 +306,8 @@ git_clone_scripts() {
set_proxy
git clone $part_cmd $url $dir
unset_proxy
exit_status=$?
unset_proxy
}
git_pull_scripts() {
+7 -13
View File
@@ -161,15 +161,7 @@ update_repo() {
echo -e "\n更新${repo_path}成功...\n"
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence"
else
echo -e "\n更新${repo_path}失败,重新下载全新仓库...\n"
rm -rf ${repo_path}
git_clone_scripts "${formatUrl}" ${repo_path} "${branch}"
if [[ $exit_status -eq 0 ]]; then
echo -e "\n更新${repo_path}成功...\n"
diff_scripts "$repo_path" "$author" "$path" "$blackword" "$dependence"
else
echo -e "\n更新${repo_path}失败,请检查网络...\n"
fi
echo -e "\n更新${repo_path}失败,请检查网络...\n"
fi
}
@@ -253,8 +245,8 @@ update_qinglong() {
local no_restart="$1"
[[ -f $dir_root/package.json ]] && ql_depend_old=$(cat $dir_root/package.json)
reset_romote_url ${dir_root} "https://github.com/whyour/qinglong.git" "master"
git_pull_scripts $dir_root "master"
reset_romote_url ${dir_root} "https://github.com/whyour/qinglong.git" ${current_branch}
git_pull_scripts $dir_root ${current_branch}
if [[ $exit_status -eq 0 ]]; then
echo -e "\n更新$dir_root成功...\n"
@@ -270,8 +262,8 @@ update_qinglong() {
local url="https://github.com/whyour/qinglong-static.git"
if [[ -d ${ql_static_repo}/.git ]]; then
reset_romote_url ${ql_static_repo} ${url} "master"
git_pull_scripts ${ql_static_repo} "master"
reset_romote_url ${ql_static_repo} ${url} ${current_branch}
git_pull_scripts ${ql_static_repo} ${current_branch}
else
git_clone_scripts ${url} ${ql_static_repo}
fi
@@ -307,6 +299,8 @@ patch_version() {
fi
git config --global pull.rebase false
cp -f /ql/.env.example /ql/.env
}
reload_pm2() {
+1 -1
View File
@@ -235,7 +235,7 @@ export default function (props: any) {
logo={
<Image
preview={false}
src="https://pic.imgdb.cn/item/61acd0dd2ab3f51d912b1986.png"
src="https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png"
/>
}
title={
+30 -16
View File
@@ -31,7 +31,7 @@ import {
import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
import { request } from '@/utils/http';
import CronModal,{ CronLabelModal } from './modal';
import CronModal, { CronLabelModal } from './modal';
import CronLogModal from './logModal';
import cron_parser from 'cron-parser';
import { diffTime } from '@/utils/date';
@@ -49,7 +49,7 @@ enum CrontabStatus {
'queued',
}
const CrontabSort: any = { 0: 0, 3: 1, 1: 2, 4: 3 };
const CrontabSort: any = { 0: 0, 5: 1, 3: 2, 1: 3, 4: 4 };
enum OperationName {
'启用',
@@ -94,21 +94,30 @@ const Crontab = ({ headerStyle, isPhone }: any) => {
)}
</a>
<span>
{record.labels?.length > 0 && record.labels[0] !== '' ?
<Popover placement='right' trigger={isPhone ? 'click' : 'hover'}
{record.labels?.length > 0 && record.labels[0] !== '' ? (
<Popover
placement="right"
trigger={isPhone ? 'click' : 'hover'}
content={
<div>
{record.labels?.map((label: string, i: number) => (
<Tag color="blue"
onClick={() => { onSearch(`label:${label}`) }}>
<Tag
color="blue"
onClick={() => {
onSearch(`label:${label}`);
}}
>
{label}
</Tag>
))}
</div>
}>
}
>
<Tag color="blue">{record.labels[0]}</Tag>
</Popover>
: ''}
) : (
''
)}
</span>
</>
),
@@ -381,14 +390,19 @@ const Crontab = ({ headerStyle, isPhone }: any) => {
setValue(
data.data
.sort((a: any, b: any) => {
const sortA = a.isDisabled ? 4 : a.status;
const sortB = b.isDisabled ? 4 : b.status;
a.isPinned = a.isPinned ? a.isPinned : 0;
b.isPinned = b.isPinned ? b.isPinned : 0;
if (a.isPinned === b.isPinned) {
return CrontabSort[sortA] - CrontabSort[sortB];
}
return b.isPinned - a.isPinned;
const sortA =
a.isPinned && a.status !== 0
? 5
: a.isDisabled && a.status !== 0
? 4
: a.status;
const sortB =
b.isPinned && b.status !== 0
? 5
: b.isDisabled && b.status !== 0
? 4
: b.status;
return CrontabSort[sortA] - CrontabSort[sortB];
})
.map((x) => {
return {
+1 -1
View File
@@ -90,7 +90,7 @@ const CronLogModal = ({
return (
<>
{(executing || loading) && <Loading3QuartersOutlined spin />}
{!executing && <CheckCircleOutlined />}
{!executing && !loading && <CheckCircleOutlined />}
<span style={{ marginLeft: 5 }}>-{cron && cron.name}</span>{' '}
</>
);
+7
View File
@@ -149,6 +149,13 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
ellipsis: {
showTitle: false,
},
sorter: {
compare: (a: any, b: any) => {
const updatedAtA = new Date(a.updatedAt || a.timestamp).getTime();
const updatedAtB = new Date(b.updatedAt || b.timestamp).getTime();
return updatedAtA - updatedAtB;
},
},
render: (text: string, record: any) => {
const language = navigator.language || navigator.languages[0];
const time = record.updatedAt || record.timestamp;
+1 -1
View File
@@ -233,7 +233,7 @@ const Initialization = () => {
<img
alt="logo"
className={styles.logo}
src="https://pic.imgdb.cn/item/61acd0dd2ab3f51d912b1986.png"
src="https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png"
/>
<span className={styles.title}></span>
</div>
+3 -1
View File
@@ -128,7 +128,7 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
value={select}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data}
placeholder="请选择日志文件"
placeholder="请选择日志"
showSearch
onSelect={onSelect}
/>,
@@ -147,6 +147,8 @@ const Log = ({ headerStyle, isPhone, theme }: any) => {
<Input.Search
className={styles['left-tree-search']}
onChange={onSearch}
placeholder="请输入日志名"
allowClear
></Input.Search>
<div className={styles['left-tree-scroller']} ref={treeDom}>
<Tree
+1 -1
View File
@@ -135,7 +135,7 @@ const Login = () => {
<img
alt="logo"
className={styles.logo}
src="https://pic.imgdb.cn/item/61acd0dd2ab3f51d912b1986.png"
src="https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png"
/>
<span className={styles.title}>
{twoFactor ? '两步验证' : config.siteName}
+3 -1
View File
@@ -413,7 +413,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
value={select}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={data}
placeholder="请选择脚本文件"
placeholder="请选择脚本"
showSearch
onSelect={onSelect}
/>,
@@ -477,6 +477,8 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
<Input.Search
className={styles['left-tree-search']}
onChange={onSearch}
placeholder="请输入脚本名"
allowClear
></Input.Search>
<div className={styles['left-tree-scroller']} ref={treeDom}>
<Tree
+49
View File
@@ -0,0 +1,49 @@
import React, { useEffect, useState } from 'react';
import { Typography, Input, Form, Button, message } from 'antd';
import styles from './index.less';
const { Link } = Typography;
const About = () => {
return (
<div className={styles.container}>
<img
alt="logo"
style={{ width: 140, marginRight: 20 }}
src="https://img.gejiba.com/images/a3f551e09ac19add4c49ec16228729c5.png"
/>
<div className={styles.right}>
<span className={styles.title}></span>
<span className={styles.desc}>
python3javaScriptshelltypescript A timed
task management panel that supports typescript, javaScript, python3,
and shell.
</span>
<div>
<Link
href="https://github.com/whyour/qinglong"
target="_blank"
style={{ marginRight: 15 }}
>
Github
</Link>
<Link
href="https://t.me/jiao_long"
target="_blank"
style={{ marginRight: 15 }}
>
Telegram频道
</Link>
<Link
href="https://github.com/whyour/qinglong/issues"
target="_blank"
>
BUG
</Link>
</div>
</div>
</div>
);
};
export default About;
+20
View File
@@ -0,0 +1,20 @@
.container {
display: flex;
justify-content: center;
align-items: center;
padding: 50px 130px;
.right {
display: flex;
justify-content: center;
flex-direction: column;
.title {
font-size: 25px;
margin-bottom: 10px;
}
.desc {
margin-bottom: 10px;
}
}
}
+5 -2
View File
@@ -32,7 +32,7 @@ import SecuritySettings from './security';
import LoginLog from './loginLog';
import NotificationSetting from './notification';
import CheckUpdate from './checkUpdate';
import { useForm } from 'antd/lib/form/Form';
import About from './about';
const { Text } = Typography;
const optionsWithDisabled = [
@@ -124,7 +124,7 @@ const Setting = ({
const [loginLogData, setLoginLogData] = useState<any[]>([]);
const [notificationInfo, setNotificationInfo] = useState<any>();
const [logRemoveFrequency, setLogRemoveFrequency] = useState<number>();
const [form] = useForm();
const [form] = Form.useForm();
const themeChange = (e: any) => {
setTheme(e.target.value);
@@ -381,6 +381,9 @@ const Setting = ({
</Form.Item>
</Form>
</Tabs.TabPane>
<Tabs.TabPane tab="关于" key="about">
<About />
</Tabs.TabPane>
</Tabs>
<AppModal
visible={isModalVisible}
+1
View File
@@ -119,6 +119,7 @@ export default {
tip: 'Bark的信息IP/设备码,例如:https://api.day.app/XXXXXXXX',
required: true,
},
{ label: 'barkIcon', tip: 'BARK推送图标,自定义推送图标 (需iOS15或以上才能显示)' },
{ label: 'barkSound', tip: 'BARK推送铃声,铃声列表去APP查看复制填写' },
{ label: 'barkGroup', tip: 'BARK推送消息的分组, 默认为qinglong' },
],
+10 -12
View File
@@ -1,13 +1,11 @@
export const version = '2.11.1';
export const changeLogLink = 'https://t.me/jiao_long/261';
export const changeLog = `2.11.1 版本说明
1. 修复openapi获取tokentoken认证
2. 修复环境变量搜索,排序,更新
3. 修复重置应用秘钥
4. 修复环境变量更新时间显示
5. 修复定时默认任务排序
6. task支持运行pyc文件,感谢https://github.com/chiupam
6. 移除ghproxy默认代理,增加新配置ProxyUrl,支持http或者socks代理。例如 http://127.0.0.1:7890
7. 更换JavaScript cdn地址
8. 其他bug修复
export const version = '2.11.3';
export const changeLogLink = 'https://t.me/jiao_long/262';
export const changeLog = `2.11.3 版本说明
1. 修复定时任务排序
2. 环境变量增加更新时间排序
3. 修复logo地址
4. 修改check命令
5. 安装依赖默认使用Proxy_url代理地址
6. 修改apk源地址
7. bark支持自定义图标,感谢 https://github.com/Ukenn2112 PR
`;
+8276 -8521
View File
File diff suppressed because it is too large Load Diff