diff --git a/.github/workflows/build_base.yml b/.github/workflows/build_base.yml deleted file mode 100644 index 1ee30b15..00000000 --- a/.github/workflows/build_base.yml +++ /dev/null @@ -1,59 +0,0 @@ -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 - context: . - file: docker/base/Dockerfile - push: true - tags: whyour/ql:base diff --git a/.umirc.ts b/.umirc.ts index 16204613..e8c58209 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -16,6 +16,10 @@ export default defineConfig({ }, favicon: '/images/g5.ico', proxy: { + '/api/public': { + target: 'http://127.0.0.1:5400/', + changeOrigin: true, + }, '/api': { target: 'http://127.0.0.1:5600/', changeOrigin: true, diff --git a/README.md b/README.md index cabb86aa..7b54c520 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,27 @@ # 待完善 ``` +### podman 部署 + +1. podman 安装 + +```bash +https://podman.io/getting-started/installation +``` + +2. 启动容器 + +```bash +podman run -dit \ + --network bridge \ + -v $PWD/ql:/ql/data \ + -p 5700:5700 \ + --name qinglong \ + --hostname qinglong \ + --restart unless-stopped \ + docker.io/whyour/qinglong:latest +``` + ### docker 部署 1. docker 安装 diff --git a/docker/Dockerfile b/docker/Dockerfile index e346ee88..2af17b8d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,5 @@ -FROM whyour/ql:base +FROM node:alpine + ARG QL_MAINTAINER="whyour" LABEL maintainer="${QL_MAINTAINER}" ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git @@ -13,17 +14,45 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ WORKDIR ${QL_DIR} -RUN git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ +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 \ + && 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 \ + && 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 \ - && cp -rf /node_modules ./ \ - && rm -rf /node_modules \ && pnpm install --prod \ && rm -rf /root/.pnpm-store \ && rm -rf /root/.cache \ + && rm -rf /root/.npm \ && git clone -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ - && cp -rf /static/* ${QL_DIR} \ + && mkdir -p ${QL_DIR}/static \ + && cp -rf /static/* ${QL_DIR}/static \ && rm -rf /static + ENTRYPOINT ["./docker/docker-entrypoint.sh"] diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile deleted file mode 100644 index d50387f9..00000000 --- a/docker/base/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -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" ] diff --git a/package.json b/package.json index da65f87e..6376fd46 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,12 @@ "start": "concurrently -n w: npm:start:*", "start:front": "umi dev", "start:back": "nodemon", + "start:public": "ts-node back/public.ts", "build:front": "umi build", "build:back": "tsc -p tsconfig.back.json", "panel": "npm run build:back && node static/build/app.js", "schedule": "npm run build:back && node static/build/schedule.js", + "public": "npm run build:back && node static/build/public.js", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "prepare": "umi generate tmp", "test": "umi-test", diff --git a/shell/bot.sh b/shell/bot.sh index 87e7f075..808b009a 100644 --- a/shell/bot.sh +++ b/shell/bot.sh @@ -22,9 +22,9 @@ if [[ ! -d ${repo_path}/.git ]]; then git_clone_scripts ${url} ${repo_path} "main" fi -cp -rf "$repo_path/jbot" $dir_root -if [[ ! -f "$dir_root/config/bot.json" ]]; then - cp -f "$repo_path/config/bot.json" "$dir_root/config" +cp -rf "$repo_path/jbot" $dir_data +if [[ ! -f "$dir_config/bot.json" ]]; then + cp -f "$repo_path/config/bot.json" "$dir_config" fi echo -e "\nbot文件下载成功...\n" @@ -32,9 +32,9 @@ echo -e "3、安装python3依赖...\n" if [[ $PipMirror ]]; then pip3 config set global.index-url $PipMirror fi -cp -f "$repo_path/jbot/requirements.txt" "$dir_root" +cp -f "$repo_path/jbot/requirements.txt" "$dir_data" -cd $dir_root +cd $dir_data cat requirements.txt | while read LREAD do if [[ ! $(pip3 show "${LREAD%%=*}" 2>/dev/null) ]]; then @@ -46,7 +46,7 @@ echo -e "\npython3依赖安装成功...\n" echo -e "4、启动bot程序...\n" make_dir $dir_log/bot -cd $dir_root +cd $dir_data ps -ef | grep "python3 -m jbot" | grep -v grep | awk '{print $1}' | xargs kill -9 2>/dev/null nohup python3 -m jbot >$dir_log/bot/nohup.log 2>&1 & echo -e "bot启动成功...\n" diff --git a/src/layouts/index.less b/src/layouts/index.less index 2362815e..5a3f9e86 100644 --- a/src/layouts/index.less +++ b/src/layouts/index.less @@ -26,6 +26,7 @@ .monaco-editor:not(.rename-box) { height: calc(100vh - 128px) !important; height: calc(100vh - var(--vh-offset, 0px) - 128px) !important; + .view-overlays .current-line { border-width: 0; } @@ -33,6 +34,7 @@ .rename-box { height: 0; + .rename-input { height: 0; padding: 0 !important; @@ -43,6 +45,7 @@ .ant-pro-grid-content.wide { max-width: unset !important; overflow: auto; + .ant-pro-page-container-children-content { overflow: auto; height: 100%; @@ -57,6 +60,7 @@ .ant-tooltip { max-width: 500px !important; + .ant-tooltip-inner { word-break: break-all !important; } @@ -72,6 +76,7 @@ .log-select { width: 250px; } + .ant-page-header-heading-left { min-width: 100px; } @@ -81,6 +86,7 @@ .config-select { width: 250px; } + .ant-page-header-heading-left { min-width: 100px; } @@ -119,10 +125,13 @@ flex-direction: column; height: calc(100vh - 48px); height: calc(100vh - var(--vh-offset, 0px) - 48px); + .ant-pro-grid-content.wide { flex: 1; + .ant-pro-grid-content-children { height: calc(100% - 36px); + > div, .log-container, .react-codemirror2, @@ -197,6 +206,7 @@ .Resizer.disabled { cursor: not-allowed; } + .Resizer.disabled:hover { border-color: transparent; } @@ -209,6 +219,7 @@ .inline-countdown.ant-statistic { display: inline-block; + .ant-statistic-content { font-size: 14px; padding: 0 3px; @@ -253,6 +264,7 @@ .ant-pro-basicLayout-content { margin: 18px; + .ant-pro-page-container { margin: -18px -18px -18px; } @@ -280,25 +292,27 @@ .side-menu-user-drop-menu { position: relative; - margin: 0; - padding: 4px 0; text-align: left; - background-clip: padding-box; outline: none; - background-color: #fff; + padding: 4px 0; + border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 4px; - box-shadow: 0 6px 16px -8px rgb(0 0 0 / 8%), 0 9px 28px 0 rgb(0 0 0 / 5%), - 0 12px 48px 16px rgb(0 0 0 / 3%); - .ant-dropdown-menu-item:hover { - color: #1890ff; - } + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + overflow: auto; + background-color: #fff; +} + +[data-dark='true'] .side-menu-user-drop-menu { + background-color: #373739; } .ant-pro-sider-logo { padding: 16px 8px !important; + h1 { margin-left: 5px !important; } + img { width: 32px !important; border-radius: 52% !important; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 2ba49ca7..1b6cc552 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -272,7 +272,7 @@ export default function (props: any) { collapsed={collapsed} rightContentRender={() => ctx.isPhone && ( - + } /> admin @@ -289,7 +289,7 @@ export default function (props: any) { }} > {!collapsed && !ctx.isPhone && ( - + } /> admin diff --git a/src/pages/crontab/detail.tsx b/src/pages/crontab/detail.tsx index e739e8b1..b2742de9 100644 --- a/src/pages/crontab/detail.tsx +++ b/src/pages/crontab/detail.tsx @@ -50,11 +50,13 @@ const CronDetailModal = ({ handleCancel, visible, theme, + isPhone, }: { cron?: any; visible: boolean; handleCancel: (needUpdate?: boolean) => void; theme: string; + isPhone: boolean; }) => { const [activeTabKey, setActiveTabKey] = useState('log'); const [loading, setLoading] = useState(true); @@ -64,6 +66,8 @@ const CronDetailModal = ({ const [isLogModalVisible, setIsLogModalVisible] = useState(false); const editorRef = useRef(null); const [scriptInfo, setScriptInfo] = useState({}); + const [logUrl, setLogUrl] = useState(''); + const [validTabs, setValidTabs] = useState(tabList); const contentList: any = { log: ( @@ -99,6 +103,8 @@ const CronDetailModal = ({ }; const onClickItem = (item: LogItem) => { + localStorage.setItem('logCron', cron.id); + setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`); request .get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`) .then((data) => { @@ -126,6 +132,7 @@ const CronDetailModal = ({ const getScript = () => { const cmd = cron.command.split(' ') as string[]; if (cmd[0] === 'task') { + setValidTabs(validTabs); if (cmd[1].startsWith('/ql/data/scripts')) { cmd[1] = cmd[1].replace('/ql/data/scripts/', ''); } @@ -141,6 +148,8 @@ const CronDetailModal = ({ .then((data) => { setValue(data.data); }); + } else { + setValidTabs([validTabs[0]]); } }; @@ -171,6 +180,7 @@ const CronDetailModal = ({ }) .then((_data: any) => { if (_data.code === 200) { + setValue(content); message.success(`保存成功`); } else { message.error(_data); @@ -198,7 +208,9 @@ const CronDetailModal = ({ title={ <> {cron.name} - + {cron.labels?.length > 0 && cron.labels[0] !== '' && ( + + )} {cron.labels?.length > 0 && cron.labels[0] !== '' && cron.labels?.map((label: string, i: number) => ( @@ -213,11 +225,17 @@ const CronDetailModal = ({ forceRender footer={false} onCancel={() => handleCancel()} - width={'80vw'} - bodyStyle={{ background: '#eee', padding: 12 }} + wrapClassName="crontab-detail" + width={!isPhone ? '80vw' : ''} > -
- +
+ +
+
任务
+
{cron.command}
+
+
+
状态
@@ -250,10 +268,6 @@ const CronDetailModal = ({ )}
-
-
任务
-
{cron.command}
-
定时
{cron.schedule}
@@ -289,8 +303,8 @@ const CronDetailModal = ({
{ onTabChange(key); @@ -306,7 +320,6 @@ const CronDetailModal = ({ ) } - bodyStyle={{ height: 'calc(80vh - 188px)', overflowY: 'auto' }} > {contentList[activeTabKey]} @@ -318,6 +331,7 @@ const CronDetailModal = ({ }} cron={cron} data={log} + logUrl={logUrl} /> ); diff --git a/src/pages/crontab/index.less b/src/pages/crontab/index.less index b127061f..44ffdf67 100644 --- a/src/pages/crontab/index.less +++ b/src/pages/crontab/index.less @@ -8,15 +8,74 @@ } } -.cron-detail-info-item { - flex: auto; +.crontab-detail { + .card-wrapper { + height: 80vh; + height: calc(80vh - var(--vh-offset, 0px)); - .cron-detail-info-title { - color: #888; + .ant-card:last-child { + .ant-card-body { + height: calc(80vh - 238px); + height: calc(80vh - var(--vh-offset, 0px) - 238px); + overflow-y: auto; + } + } } - .cron-detail-info-value { - margin-top: 18px; + .ant-modal-body { + background: #eee; + padding: 12px; + } + + .ant-card-body { + padding: 18px; + } + .ant-card-head { + padding: 0 18px; + } + + .ant-card:first-child { + max-height: 66px; + overflow: auto; + + .ant-card-body { + min-width: 600px; + } + + .cron-detail-info-item { + display: flex; + + .cron-detail-info-title { + width: 50px; + } + + .cron-detail-info-value { + flex: 1; + margin-top: 0; + } + } + } + + .ant-card:nth-child(2) { + overflow-x: auto; + + .ant-card-body { + display: flex; + justify-content: space-between; + min-width: 600px; + } + } + + .cron-detail-info-item { + flex: auto; + + .cron-detail-info-title { + color: #888; + } + + .cron-detail-info-value { + margin-top: 12px; + } } } diff --git a/src/pages/crontab/index.tsx b/src/pages/crontab/index.tsx index 03b43ce4..2a52ca96 100644 --- a/src/pages/crontab/index.tsx +++ b/src/pages/crontab/index.tsx @@ -40,7 +40,7 @@ import { getTableScroll } from '@/utils/index'; import { history } from 'umi'; import './index.less'; -const { Text } = Typography; +const { Text, Paragraph } = Typography; const { Search } = Input; export enum CrontabStatus { @@ -85,28 +85,32 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { goToScriptManager(record); }} > - - {record.labels?.map((label: string) => ( - { - e.stopPropagation(); - setSearchText(`label:${label}`); - }} - > - {label} - - ))} -
- } - > - {record.name || '-'} - + {record.labels?.length > 0 && record.labels[0] !== '' ? ( + + {record.labels?.map((label: string) => ( + { + e.stopPropagation(); + setSearchValue(`label:${label}`); + }} + > + {label} + + ))} +
+ } + > + {record.name || '-'} + + ) : ( + record.name || '-' + )} {record.isPinned ? ( @@ -130,16 +134,16 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { align: 'center' as const, render: (text: string, record: any) => { return ( - {text} - + ); }, sorter: { @@ -364,6 +368,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { const [tableScrollHeight, setTableScrollHeight] = useState(); const [isDetailModalVisible, setIsDetailModalVisible] = useState(false); const [detailCron, setDetailCron] = useState(); + const [searchValue, setSearchValue] = useState(''); const goToScriptManager = (record: any) => { const cmd = record.command.split(' ') as string[]; @@ -627,7 +632,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => { index: number; }> = ({ record, index }) => ( { enterButton allowClear loading={loading} - value={searchText} + value={searchValue} + onChange={(e) => setSearchValue(e.target.value)} onSearch={onSearch} />,