Merge branch 'dev' of github.com:engigu/baihu into dev

This commit is contained in:
engigu
2026-02-07 09:24:40 +08:00
69 changed files with 420 additions and 234 deletions
+47 -7
View File
@@ -6,7 +6,7 @@ on:
branches:
- main
paths:
- 'Dockerfile.base'
- 'docker/Dockerfile.base'
env:
REGISTRY: ghcr.io
@@ -17,9 +17,39 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
base_type: [debian, alpine, debian13]
platform:
- linux/amd64
- linux/arm64
include:
- base_type: debian
platform: linux/arm/v7
dockerfile: docker/Dockerfile.base
tag_suffix: ""
- base_type: debian
platform: linux/amd64
dockerfile: docker/Dockerfile.base
tag_suffix: ""
- base_type: debian
platform: linux/arm64
dockerfile: docker/Dockerfile.base
tag_suffix: ""
- base_type: alpine
platform: linux/amd64
dockerfile: docker/Dockerfile.alpine.base
tag_suffix: "-alpine"
- base_type: alpine
platform: linux/arm64
dockerfile: docker/Dockerfile.alpine.base
tag_suffix: "-alpine"
- base_type: debian13
platform: linux/amd64
dockerfile: docker/Dockerfile.debian13.base
tag_suffix: "-debian13"
- base_type: debian13
platform: linux/arm64
dockerfile: docker/Dockerfile.debian13.base
tag_suffix: "-debian13"
permissions:
contents: read
@@ -33,7 +63,7 @@ jobs:
id: platform
run: |
platform=${{ matrix.platform }}
echo "pair=${platform//\//-}" >> $GITHUB_OUTPUT
echo "pair=${platform//\//-}-${{ matrix.base_type }}" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -55,14 +85,14 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=base
type=raw,value=base${{ matrix.tag_suffix }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.base
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
@@ -87,6 +117,16 @@ jobs:
merge:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
base_type: [debian, alpine, debian13]
include:
- base_type: debian
tag: base
- base_type: alpine
tag: base-alpine
- base_type: debian13
tag: base-debian13
permissions:
contents: read
@@ -100,7 +140,7 @@ jobs:
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
pattern: digests-*-${{ matrix.base_type }}
merge-multiple: true
- name: Set up Docker Buildx
@@ -120,7 +160,7 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=base
type=raw,value=${{ matrix.tag }}
- name: Create manifest list and push
working-directory: /tmp/digests
@@ -130,4 +170,4 @@ jobs:
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:base
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ matrix.tag }}
+26 -10
View File
@@ -12,6 +12,19 @@ env:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
base_type: [debian, alpine, debian13]
include:
- base_type: debian
base_tag: base
tag_suffix: ""
- base_type: alpine
base_tag: base-alpine
tag_suffix: "-alpine"
- base_type: debian13
base_tag: base-debian13
tag_suffix: "-debian13"
permissions:
contents: read
@@ -38,10 +51,9 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}-amd64
type=raw,value=latest-amd64,enable={{is_default_branch}}
type=ref,event=branch,suffix=-amd64
type=sha,prefix={{branch}}-,suffix=-amd64
type=raw,value=latest-amd64${{ matrix.tag_suffix }},enable={{is_default_branch}}
type=ref,event=branch,suffix=-amd64${{ matrix.tag_suffix }}
type=sha,prefix={{branch}}-,suffix=-amd64${{ matrix.tag_suffix }}
- name: Get build time
id: build_time
@@ -51,6 +63,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
@@ -58,11 +71,13 @@ jobs:
build-args: |
VERSION=${{ github.ref_name }}
BUILD_TIME=${{ steps.build_time.outputs.time }}
cache-from: type=gha,scope=linux-amd64
cache-to: type=gha,scope=linux-amd64,mode=max
BASE_TAG=${{ matrix.base_tag }}
cache-from: type=gha,scope=linux-amd64-${{ matrix.base_type }}
cache-to: type=gha,scope=linux-amd64-${{ matrix.base_type }},mode=max
provenance: false
- name: Deploy to Demo server
if: github.ref == 'refs/heads/main' && matrix.base_type == 'debian'
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.DEPLOY_HOST }}
@@ -71,10 +86,11 @@ jobs:
port: ${{ secrets.DEPLOY_PORT }}
script: |
cd ${{ secrets.DEPLOY_PATH }}
docker-compose pull
docker-compose up -d
docker-compose -f docker/docker-compose.yml pull
docker-compose -f docker/docker-compose.yml up -d
- name: Deploy to X server
if: github.ref == 'refs/heads/main' && matrix.base_type == 'debian'
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.DEPLOY_HOST }}
@@ -83,5 +99,5 @@ jobs:
port: ${{ secrets.DEPLOY_PORT }}
script: |
cd ${{ secrets.DEPLOY_X_PATH }}
docker-compose pull
docker-compose up -d
docker-compose -f docker/docker-compose.yml pull
docker-compose -f docker/docker-compose.yml up -d
+20 -4
View File
@@ -12,6 +12,19 @@ env:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
base_type: [debian, alpine, debian13]
include:
- base_type: debian
base_tag: base
tag_suffix: ""
- base_type: alpine
base_tag: base-alpine
tag_suffix: "-alpine"
- base_type: debian13
base_tag: base-debian13
tag_suffix: "-debian13"
permissions:
contents: read
@@ -38,8 +51,9 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}-amd64
type=raw,value=latest-amd64,enable={{is_default_branch}}
type=semver,pattern={{version}}-amd64${{ matrix.tag_suffix }}
type=raw,value=latest-amd64${{ matrix.tag_suffix }},enable={{is_default_branch}}
type=ref,event=branch,suffix=-amd64${{ matrix.tag_suffix }}
- name: Get build time
id: build_time
@@ -49,6 +63,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
@@ -56,6 +71,7 @@ jobs:
build-args: |
VERSION=${{ github.ref_name }}
BUILD_TIME=${{ steps.build_time.outputs.time }}
cache-from: type=gha,scope=linux-amd64
cache-to: type=gha,scope=linux-amd64,mode=max
BASE_TAG=${{ matrix.base_tag }}
cache-from: type=gha,scope=linux-amd64-${{ matrix.base_type }}
cache-to: type=gha,scope=linux-amd64-${{ matrix.base_type }},mode=max
provenance: false
+20 -4
View File
@@ -12,6 +12,19 @@ env:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
base_type: [debian, alpine, debian13]
include:
- base_type: debian
base_tag: base
tag_suffix: ""
- base_type: alpine
base_tag: base-alpine
tag_suffix: "-alpine"
- base_type: debian13
base_tag: base-debian13
tag_suffix: "-debian13"
permissions:
contents: read
@@ -41,8 +54,9 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}-arm64
type=raw,value=latest-arm64,enable={{is_default_branch}}
type=semver,pattern={{version}}-arm64${{ matrix.tag_suffix }}
type=raw,value=latest-arm64${{ matrix.tag_suffix }},enable={{is_default_branch}}
type=ref,event=branch,suffix=-arm64${{ matrix.tag_suffix }}
- name: Get build time
id: build_time
@@ -52,6 +66,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
@@ -59,6 +74,7 @@ jobs:
build-args: |
VERSION=${{ github.ref_name }}
BUILD_TIME=${{ steps.build_time.outputs.time }}
cache-from: type=gha,scope=linux-arm64
cache-to: type=gha,scope=linux-arm64,mode=max
BASE_TAG=${{ matrix.base_tag }}
cache-from: type=gha,scope=linux-arm64-${{ matrix.base_type }}
cache-to: type=gha,scope=linux-arm64-${{ matrix.base_type }},mode=max
provenance: false
+48 -5
View File
@@ -14,9 +14,39 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
base_type: [debian, alpine, debian13]
platform:
- linux/amd64
- linux/arm64
include:
- base_type: debian
platform: linux/arm/v7
base_tag: base
tag_suffix: ""
- base_type: debian
platform: linux/amd64
base_tag: base
tag_suffix: ""
- base_type: debian
platform: linux/arm64
base_tag: base
tag_suffix: ""
- base_type: alpine
platform: linux/amd64
base_tag: base-alpine
tag_suffix: "-alpine"
- base_type: alpine
platform: linux/arm64
base_tag: base-alpine
tag_suffix: "-alpine"
- base_type: debian13
platform: linux/amd64
base_tag: base-debian13
tag_suffix: "-debian13"
- base_type: debian13
platform: linux/arm64
base_tag: base-debian13
tag_suffix: "-debian13"
permissions:
contents: read
@@ -30,7 +60,7 @@ jobs:
id: platform
run: |
platform=${{ matrix.platform }}
echo "pair=${platform//\//-}" >> $GITHUB_OUTPUT
echo "pair=${platform//\//-}-${{ matrix.base_type }}" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -61,10 +91,12 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
VERSION=${{ github.ref_name }}
BUILD_TIME=${{ steps.build_time.outputs.time }}
BASE_TAG=${{ matrix.base_tag }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ steps.platform.outputs.pair }}
@@ -88,6 +120,16 @@ jobs:
merge:
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
base_type: [debian, alpine, debian13]
include:
- base_type: debian
tag_suffix: ""
- base_type: alpine
tag_suffix: "-alpine"
- base_type: debian13
tag_suffix: "-debian13"
permissions:
contents: write
@@ -101,7 +143,7 @@ jobs:
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
pattern: digests-*-${{ matrix.base_type }}
merge-multiple: true
- name: Set up Docker Buildx
@@ -121,8 +163,8 @@ jobs:
images: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
type=semver,pattern={{version}}${{ matrix.tag_suffix }}
type=raw,value=latest${{ matrix.tag_suffix }}
- name: Create manifest list and push
working-directory: /tmp/digests
@@ -132,9 +174,10 @@ jobs:
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}${{ matrix.tag_suffix }}
- name: Create GitHub Release
if: matrix.base_type == 'debian'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
+1
View File
@@ -48,3 +48,4 @@ configs/config.local.json
__pycache__/
# Misc
.stfolder
+3
View File
@@ -0,0 +1,3 @@
.git/
web/node_modules
web/dist
+4 -4
View File
@@ -6,7 +6,7 @@ GOGET=go get
GOMOD=go mod
VERSION=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
BUILD_TIME=$(shell date '+%Y/%m/%d %H:%M:%S')
LDFLAGS=-ldflags="-s -w -X 'baihu/internal/constant.Version=$(VERSION)' -X 'baihu/internal/constant.BuildTime=$(BUILD_TIME)'"
LDFLAGS=-ldflags="-s -w -X 'github.com/engigu/baihu-panel/internal/constant.Version=$(VERSION)' -X 'github.com/engigu/baihu-panel/internal/constant.BuildTime=$(BUILD_TIME)'"
# Default target
all: build
@@ -66,7 +66,7 @@ deps:
# Docker build
docker-build:
docker build -t $(BINARY) .
docker build -t $(BINARY) -f docker/Dockerfile .
# Docker run
docker-run:
@@ -74,11 +74,11 @@ docker-run:
# Docker compose up
docker-up:
docker-compose up -d
docker-compose -f docker/docker-compose.yml up -d
# Docker compose down
docker-down:
docker-compose down
docker-compose -f docker/docker-compose.yml down
# Help
help:
+10 -1
View File
@@ -1,7 +1,6 @@
# 白虎面板
[![Hits](https://hits.sh/github.com/engigu/baihu-panel.svg?view=today-total)](https://hits.sh/github.com/engigu/baihu-panel/)
[![Image Tags](https://ghcr-badge.egpl.dev/engigu/baihu/tags?color=%2344cc11&ignore=latest%2Cmain*%2Cdev&n=3&label=docker+tags&trim=)](https://ghcr-badge.egpl.dev/engigu/baihu/tags)
![Latest Version](https://ghcr-badge.egpl.dev/engigu/baihu/latest_tag?color=%2344cc11&ignore=latest%2Cmain*&label=docker+version&trim=)
![Image Size](https://ghcr-badge.egpl.dev/engigu/baihu/size?color=%2344cc11&tag=latest&label=docker+image&trim=)
![Image pulls](https://img.shields.io/badge/dynamic/json?url=https://ghcr-badge.elias.eu.org/api/engigu/baihu-panel/baihu&query=downloadCount&style=flat&label=docker%20pulls&color=44cc11)
@@ -10,6 +9,14 @@
演示站点(演示站点的服务器比较烂,见谅) [演示站点](https://baihu-demo-site.qwapi.eu.org/)
## 更新日志 ☕
### 最近更新
**2026.02.06** - 整理 Docker 目录结构,增加 Alpine 及 Debian 13 (Trixie) 镜像支持
[查看完整更新日志](./CHANGELOG.md)
## 项目来由
多少和青龙面板有点关系,我自己也是青龙面板的使用者,但是现在的青龙面板性能我觉得有点难以接受。以我自己的使用(`机器1C2G`)为例,一个`python``requests`脚本每隔`30s`执行一次,有时候cpu执行的时候能跳变到`50%`以上。可以看看下面gif图片(如果不动,点击图片查看)
@@ -24,6 +31,8 @@
![baihu.gif](https://f.pz.al/pzal/2025/12/24/f0d171f9a686d.gif)
如果项目有用,请帮忙点个star。
## 特色
- **轻量级:** docker/compose部署,无需复杂配置,开箱即用
+6 -4
View File
@@ -1,7 +1,8 @@
ARG BASE_TAG=base
# ================================
# Stage 1: Build frontend
# ================================
FROM node:20-alpine AS frontend-builder
FROM --platform=$BUILDPLATFORM node:20-alpine AS frontend-builder
WORKDIR /app/web
@@ -59,7 +60,7 @@ COPY --from=frontend-builder /app/web/dist ./internal/static/dist
RUN VERSION_VAL=$(cat /build-info/version.txt) && \
BUILD_TIME_VAL=$(cat /build-info/build_time.txt) && \
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -ldflags="-s -w -X baihu/internal/constant.Version=${VERSION_VAL} -X 'baihu/internal/constant.BuildTime=${BUILD_TIME_VAL}'" \
go build -ldflags="-s -w -X github.com/engigu/baihu-panel/internal/constant.Version=${VERSION_VAL} -X 'github.com/engigu/baihu-panel/internal/constant.BuildTime=${BUILD_TIME_VAL}'" \
-o baihu .
# ================================
@@ -104,7 +105,8 @@ RUN VERSION_VAL=$(cat /build-info/version.txt) && \
# ================================
# Stage 4: Final image
# ================================
FROM ghcr.io/engigu/baihu:base
ARG BASE_TAG
FROM ghcr.io/engigu/baihu:${BASE_TAG}
ENV TZ=Asia/Shanghai
ENV PATH="/app/envs/node/bin:/app/envs/python/bin:$PATH"
@@ -130,7 +132,7 @@ COPY --from=frontend-builder /app/web/dist ./internal/static/dist
# Copy configs and entrypoint
COPY --from=backend-builder /app/configs ./configs
COPY docker-entrypoint.sh .
COPY docker/docker-entrypoint.sh .
# Copy sync.py to /opt
COPY custom/sync.py /opt/sync.py
+24
View File
@@ -0,0 +1,24 @@
FROM alpine:3.19
ENV TZ=Asia/Shanghai
ENV PATH="/app/envs/node/bin:/app/envs/python/bin:$PATH"
ENV NODE_PATH="/app/envs/node/lib/node_modules"
# 安装必要系统工具 + Node + Python
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& apk add --no-cache \
tzdata \
git \
gcc \
musl-dev \
curl \
wget \
vim \
nodejs \
npm \
python3 \
py3-pip \
htop \
&& cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo "${TZ}" > /etc/timezone \
&& rm -rf /var/cache/apk/*
+15
View File
@@ -0,0 +1,15 @@
FROM debian:trixie-slim
ENV TZ=Asia/Shanghai
ENV PATH="/app/envs/node/bin:/app/envs/python/bin:$PATH"
ENV NODE_PATH="/app/envs/node/lib/node_modules"
# 安装必要系统工具 + Node + Python
RUN sed -i 's@deb.debian.org@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list.d/debian.sources \
&& sed -i 's@security.debian.org@mirrors.tuna.tsinghua.edu.cn@g' /etc/apt/sources.list.d/debian.sources \
&& echo "${TZ}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
tzdata git gcc curl wget vim nodejs htop npm python3 python3-venv python3-pip \
&& rm -rf /var/lib/apt/lists/*
+3 -3
View File
@@ -1,4 +1,4 @@
module baihu
module github.com/engigu/baihu-panel
go 1.24.0
@@ -11,8 +11,10 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/gorilla/websocket v1.5.3
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/sirupsen/logrus v1.9.3
golang.org/x/text v0.32.0
gopkg.in/ini.v1 v1.67.0
gorm.io/driver/mysql v1.6.0
gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.31.1
@@ -51,7 +53,6 @@ require (
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
@@ -64,7 +65,6 @@ require (
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
+6 -4
View File
@@ -41,9 +41,10 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
@@ -100,6 +101,8 @@ github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/i
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -111,8 +114,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -144,8 +148,6 @@ golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+5 -5
View File
@@ -4,11 +4,11 @@ import (
"fmt"
"os"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/router"
"baihu/internal/services"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/router"
"github.com/engigu/baihu-panel/internal/services"
"github.com/gin-gonic/gin"
)
+3 -3
View File
@@ -3,9 +3,9 @@ package cache
import (
"sync"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
// siteCache 站点设置内存缓存
+7 -4
View File
@@ -18,9 +18,9 @@ type Sentence struct {
}
var (
lineCount int
lineOffsets []int64
sentenceOnce sync.Once
lineCount int
lineOffsets []int64
sentenceOnce sync.Once
)
// initSentences 初始化:统计行数和记录每行偏移
@@ -44,13 +44,16 @@ func GetRandomSentence() string {
}
targetLine := rand.Intn(lineCount)
scanner := bufio.NewScanner(bytes.NewReader(sentenceData))
currentLine := 0
for scanner.Scan() {
if currentLine == targetLine {
var s Sentence
if err := json.Unmarshal(scanner.Bytes(), &s); err == nil && s.Name != "" {
if s.From != "" {
return "\"" + s.Name + "\"—— " + s.From
}
return s.Name
}
break
+4 -4
View File
@@ -1,10 +1,10 @@
package controllers
import (
"baihu/internal/logger"
"baihu/internal/models"
"baihu/internal/services"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"encoding/json"
"net/http"
"strconv"
+4 -4
View File
@@ -3,10 +3,10 @@ package controllers
import (
"strconv"
"baihu/internal/constant"
"baihu/internal/middleware"
"baihu/internal/services"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/middleware"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+5 -5
View File
@@ -4,11 +4,11 @@ import (
"sort"
"time"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"baihu/internal/services/tasks"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
@@ -4,9 +4,9 @@ import (
"strconv"
"strings"
"baihu/internal/models"
"baihu/internal/services"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package controllers
import (
"strconv"
"baihu/internal/services"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package controllers
import (
"strconv"
"baihu/internal/services/tasks"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"path/filepath"
"strings"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+3 -3
View File
@@ -3,9 +3,9 @@ package controllers
import (
"strconv"
"baihu/internal/database"
"baihu/internal/models"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+2 -2
View File
@@ -3,8 +3,8 @@ package controllers
import (
"strconv"
"baihu/internal/services"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+6 -6
View File
@@ -5,12 +5,12 @@ import (
"runtime"
"strconv"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"baihu/internal/services"
"baihu/internal/services/tasks"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
"fmt"
"os"
"time"
+4 -4
View File
@@ -4,10 +4,10 @@ import (
"path/filepath"
"strconv"
"baihu/internal/constant"
"baihu/internal/services"
"baihu/internal/services/tasks"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+2 -2
View File
@@ -10,8 +10,8 @@ import (
"sync"
"unicode/utf8"
"baihu/internal/constant"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/creack/pty"
"github.com/gin-gonic/gin"
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"baihu/internal/logger"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/glebarez/sqlite"
"gorm.io/driver/mysql"
+2 -2
View File
@@ -1,8 +1,8 @@
package database
import (
"baihu/internal/logger"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
func Migrate() error {
+2 -2
View File
@@ -1,8 +1,8 @@
package middleware
import (
"baihu/internal/constant"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
)
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"baihu/internal/logger"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/gin-gonic/gin"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
"gorm.io/gorm"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
"time"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
"gorm.io/gorm"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
)
// LoginLog 登录日志
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
)
// SendStats 任务执行统计
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
)
// Setting 系统设置
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
"gorm.io/gorm"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package models
import (
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/constant"
"gorm.io/gorm"
)
+4 -4
View File
@@ -1,10 +1,10 @@
package router
import (
"baihu/internal/constant"
"baihu/internal/controllers"
"baihu/internal/services"
"baihu/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/controllers"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/services/tasks"
)
var cronService *tasks.CronService
+4 -4
View File
@@ -5,10 +5,10 @@ import (
"net/http"
"strings"
"baihu/internal/controllers"
"baihu/internal/middleware"
"baihu/internal/services"
"baihu/internal/static"
"github.com/engigu/baihu-panel/internal/controllers"
"github.com/engigu/baihu-panel/internal/middleware"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/static"
"github.com/gin-gonic/gin"
)
+5 -5
View File
@@ -1,11 +1,11 @@
package services
import (
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"baihu/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services/tasks"
"crypto/rand"
"encoding/hex"
"fmt"
+3 -3
View File
@@ -1,9 +1,9 @@
package services
import (
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"encoding/json"
"sync"
"time"
+3 -3
View File
@@ -9,9 +9,9 @@ import (
"path/filepath"
"time"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type BackupService struct {
+3 -3
View File
@@ -1,8 +1,8 @@
package services
import (
"baihu/internal/constant"
"baihu/internal/logger"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/logger"
"os"
"strconv"
@@ -67,7 +67,7 @@ func LoadConfig(path string) (*AppConfig, error) {
Port: 3306,
User: "root",
Password: "",
DBName: "baihu",
DBName: "github.com/engigu/baihu-panel",
Path: constant.DefaultDBPath,
TablePrefix: "baihu_",
},
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"os/exec"
"strings"
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type DependencyService struct{}
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"strconv"
"strings"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type EnvService struct{}
+1 -1
View File
@@ -1,7 +1,7 @@
package services
import (
"baihu/internal/logger"
"github.com/engigu/baihu-panel/internal/logger"
)
type InitService struct {
+2 -2
View File
@@ -1,8 +1,8 @@
package services
import (
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type LoginLogService struct{}
+2 -2
View File
@@ -1,8 +1,8 @@
package services
import (
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type ScriptService struct{}
+2 -2
View File
@@ -3,8 +3,8 @@ package services
import (
"time"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type SendStatsService struct{}
+4 -4
View File
@@ -1,10 +1,10 @@
package services
import (
"baihu/internal/cache"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/cache"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type SettingsService struct{}
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"sync"
"time"
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/robfig/cron/v3"
)
+3 -3
View File
@@ -1,9 +1,9 @@
package tasks
import (
"baihu/internal/constant"
"baihu/internal/logger"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/utils"
"bytes"
"context"
"fmt"
@@ -1,11 +1,11 @@
package tasks
import (
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/utils"
"bytes"
"context"
"encoding/json"
+4 -4
View File
@@ -1,10 +1,10 @@
package tasks
import (
"baihu/internal/database"
"baihu/internal/logger"
"baihu/internal/models"
"baihu/internal/utils"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/utils"
"encoding/json"
"time"
)
+2 -2
View File
@@ -1,8 +1,8 @@
package tasks
import (
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type TaskService struct{}
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"crypto/sha256"
"encoding/hex"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
)
type UserService struct{}
+2 -2
View File
@@ -3,8 +3,8 @@ package utils
import (
"strconv"
"baihu/internal/cache"
"baihu/internal/constant"
"github.com/engigu/baihu-panel/internal/cache"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/gin-gonic/gin"
)
+1 -1
View File
@@ -1,6 +1,6 @@
package main
import "baihu/internal/bootstrap"
import "github.com/engigu/baihu-panel/internal/bootstrap"
func main() {
bootstrap.New().Run()
+22 -28
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import { RouterLink, RouterView, useRoute } from 'vue-router'
import { resetAuthCache } from '@/router'
import { LayoutDashboard, ListTodo, FileCode, Settings, LogOut, ScrollText, Terminal, Variable, KeyRound, Package, Menu, X, Server } from 'lucide-vue-next'
@@ -17,7 +17,7 @@ function loadSentenceFromCache(): string | null {
try {
const cached = localStorage.getItem(SENTENCE_CACHE_KEY)
const cacheTime = localStorage.getItem(SENTENCE_CACHE_TIME_KEY)
if (cached && cacheTime) {
const age = Date.now() - parseInt(cacheTime)
// 使
@@ -46,6 +46,10 @@ const cachedSentence = loadSentenceFromCache()
const sentence = ref(cachedSentence || '欢迎使用白虎面板')
const { siteSettings, loadSettings } = useSiteSettings()
const mobileMenuOpen = ref(false)
const sentenceContent = computed(() => {
const match = sentence.value.match(/^"(.+)"—— /)
return match ? match[1] : sentence.value
})
const navItems = [
{ to: '/', icon: LayoutDashboard, label: '数据仪表', exact: true },
@@ -101,19 +105,13 @@ onMounted(() => {
<template>
<div class="flex h-screen bg-muted/40">
<!-- Mobile Menu Overlay -->
<div
v-if="mobileMenuOpen"
class="fixed inset-0 bg-black/50 z-40 lg:hidden"
@click="mobileMenuOpen = false"
/>
<div v-if="mobileMenuOpen" class="fixed inset-0 bg-black/50 z-40 lg:hidden" @click="mobileMenuOpen = false" />
<!-- Sidebar -->
<aside
:class="[
'fixed lg:static inset-y-0 left-0 z-50 w-44 border-r bg-background flex flex-col transform transition-transform duration-200 ease-in-out lg:transform-none',
mobileMenuOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
]"
>
<aside :class="[
'fixed lg:static inset-y-0 left-0 z-50 w-44 border-r bg-background flex flex-col transform transition-transform duration-200 ease-in-out lg:transform-none',
mobileMenuOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
]">
<div class="h-14 flex items-center justify-center px-4 font-semibold text-lg border-b relative">
<span>{{ siteSettings.title }}</span>
<Button variant="ghost" size="icon" class="h-8 w-8 lg:hidden absolute right-2" @click="mobileMenuOpen = false">
@@ -121,25 +119,18 @@ onMounted(() => {
</Button>
</div>
<nav class="flex-1 px-3 py-6 space-y-1 flex flex-col items-center">
<RouterLink
v-for="item in navItems"
:key="item.to"
:to="item.to"
custom
v-slot="{ navigate }"
>
<Button
variant="ghost"
<RouterLink v-for="item in navItems" :key="item.to" :to="item.to" custom v-slot="{ navigate }">
<Button variant="ghost"
:class="['justify-start gap-3 h-9 px-3', isItemActive(item) && 'bg-accent text-accent-foreground']"
@click="handleNavClick(navigate)"
>
@click="handleNavClick(navigate)">
<component :is="item.icon" class="h-4 w-4" />
{{ item.label }}
</Button>
</RouterLink>
</nav>
<div class="px-3 py-4 border-t flex justify-center">
<Button variant="ghost" class="justify-start gap-3 h-9 px-3 text-muted-foreground hover:text-foreground" @click="logout">
<Button variant="ghost" class="justify-start gap-3 h-9 px-3 text-muted-foreground hover:text-foreground"
@click="logout">
<LogOut class="h-4 w-4" />
退出登录
</Button>
@@ -149,11 +140,14 @@ onMounted(() => {
<!-- Main Content -->
<main class="flex-1 overflow-auto w-full">
<div class="h-14 border-b bg-background flex items-center justify-between px-4 lg:px-6">
<div class="flex items-center gap-3">
<Button variant="ghost" size="icon" class="h-8 w-8 lg:hidden" @click="mobileMenuOpen = true">
<div class="flex items-center gap-3 flex-1 min-w-0">
<Button variant="ghost" size="icon" class="h-8 w-8 lg:hidden shrink-0" @click="mobileMenuOpen = true">
<Menu class="h-5 w-5" />
</Button>
<span class="text-sm text-muted-foreground truncate max-w-[200px] sm:max-w-none">{{ sentence }}</span>
<span class="text-sm text-muted-foreground truncate" :title="sentence">
<span class="hidden sm:inline">{{ sentence }}</span>
<span class="sm:hidden">{{ sentenceContent }}</span>
</span>
</div>
<ThemeToggle />
</div>
+32 -28
View File
@@ -37,23 +37,23 @@ function animateNumber(key: keyof Stats, target: number) {
const start = displayStats.value[key]
const duration = 800 //
const startTime = performance.now()
function update(currentTime: number) {
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
// 使 easeOutCubic
const easeProgress = 1 - Math.pow(1 - progress, 3)
displayStats.value[key] = Math.round(start + (target - start) * easeProgress)
if (progress < 1) {
requestAnimationFrame(update)
} else {
displayStats.value[key] = target
}
}
requestAnimationFrame(update)
}
@@ -79,7 +79,7 @@ function getGridColor() {
function handleThemeChange() {
//
if (isInitializing) return
const newIsDark = document.documentElement.classList.contains('dark')
if (newIsDark !== isDark.value) {
isDark.value = newIsDark
@@ -127,11 +127,11 @@ async function reloadCharts() {
// Vue DOM
await nextTick()
await new Promise(resolve => setTimeout(resolve, 100))
//
const statsChart = document.querySelector("#stats-chart")
const pieChartEl = document.querySelector("#pie-chart")
if (statsChart && pieChartEl && statsChart.parentElement && pieChartEl.parentElement) {
renderLineChart()
renderPieChart()
@@ -143,15 +143,15 @@ async function reloadCharts() {
const renderLineChart = () => {
const container = document.querySelector("#stats-chart")
if (!container || !container.parentElement) return
if (lineChart) {
lineChart.destroy()
lineChart = null
}
//
container.innerHTML = ''
const textColor = getTextColor()
const gridColor = getGridColor()
const options = {
@@ -282,18 +282,18 @@ const renderLineChart = () => {
const renderPieChart = () => {
if (taskStats.value.length === 0) return
const container = document.querySelector("#pie-chart")
if (!container || !container.parentElement) return
if (pieChart) {
pieChart.destroy()
pieChart = null
}
//
container.innerHTML = ''
const totalCount = taskStats.value.reduce((sum, item) => sum + item.count, 0)
const textColor = getTextColor()
const options = {
@@ -366,13 +366,13 @@ const renderPieChart = () => {
dataLabels: {
enabled: true,
formatter: (val: number) => val.toFixed(1) + '%',
style: {
fontSize: '11px',
fontFamily: 'Inter, sans-serif',
fontWeight: 'bold',
style: {
fontSize: '11px',
fontFamily: 'Inter, sans-serif',
fontWeight: 'bold',
colors: ['#ffffff']
},
dropShadow: {
dropShadow: {
enabled: true,
top: 1,
left: 1,
@@ -395,7 +395,7 @@ const renderPieChart = () => {
onMounted(async () => {
window.addEventListener('resize', handleResize)
try {
const [statsData, sendStatsData, taskStatsData] = await Promise.all([
api.dashboard.stats(),
@@ -405,13 +405,13 @@ onMounted(async () => {
updateStats(statsData)
sendStats.value = sendStatsData
taskStats.value = taskStatsData
//
setTimeout(() => {
renderLineChart()
renderPieChart()
chartsLoaded.value = true
//
setTimeout(() => {
isInitializing = false
@@ -422,7 +422,7 @@ onMounted(async () => {
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
}, 500)
}, 100)
} catch {}
} catch { }
})
onUnmounted(() => {
@@ -448,7 +448,8 @@ onUnmounted(() => {
</div>
<div class="grid gap-4 grid-cols-2 sm:grid-cols-3 lg:grid-cols-6">
<Card v-for="item in statItems" :key="item.key" class="cursor-pointer hover:bg-accent/50 transition-colors" @click="navigateTo(item.route)">
<Card v-for="item in statItems" :key="item.key" class="cursor-pointer hover:bg-accent/50 transition-colors"
@click="navigateTo(item.route)">
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-xs sm:text-sm font-medium">{{ item.label }}</CardTitle>
<component :is="item.icon" class="h-4 w-4 text-muted-foreground" />
@@ -467,7 +468,8 @@ onUnmounted(() => {
</CardHeader>
<CardContent class="relative h-[300px]">
<div id="stats-chart" class="w-full h-full"></div>
<div v-if="!chartsLoaded" class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
<div v-if="!chartsLoaded"
class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
加载中...
</div>
</CardContent>
@@ -480,10 +482,12 @@ onUnmounted(() => {
</CardHeader>
<CardContent class="relative h-[300px]">
<div id="pie-chart" class="w-full h-full"></div>
<div v-if="!chartsLoaded" class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
<div v-if="!chartsLoaded"
class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
加载中...
</div>
<div v-else-if="taskStats.length === 0" class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
<div v-else-if="taskStats.length === 0"
class="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm bg-card">
暂无数据
</div>
</CardContent>
+5 -7
View File
@@ -6,7 +6,6 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Badge } from '@/components/ui/badge'
import DirTreeSelect from '@/components/DirTreeSelect.vue'
import { Plus, ChevronDown, X } from 'lucide-vue-next'
import { api, type Task, type EnvVar, type Agent } from '@/api'
@@ -132,8 +131,7 @@ function addEnv(id: number) {
}
function removeEnv(id: number) {
const idx = selectedEnvIds.value.indexOf(id)
if (idx !== -1) selectedEnvIds.value.splice(idx, 1)
selectedEnvIds.value = selectedEnvIds.value.filter(envId => envId !== id)
}
async function save() {
@@ -266,10 +264,10 @@ async function save() {
</PopoverContent>
</Popover>
<div v-if="selectedEnvs.length > 0" class="flex flex-wrap gap-1">
<Badge v-for="env in selectedEnvs" :key="env.id" variant="secondary" class="gap-0.5 pr-0.5 text-xs h-5">
{{ env.name }}
<X class="h-2.5 w-2.5 cursor-pointer hover:text-destructive" @click.stop="removeEnv(env.id)" />
</Badge>
<div v-for="env in selectedEnvs" :key="env.id" class="inline-flex items-center gap-0.5 px-2 py-0.5 text-xs h-5 rounded-full bg-secondary text-secondary-foreground">
<span>{{ env.name }}</span>
<X class="h-2.5 w-2.5 cursor-pointer hover:text-destructive" @click="removeEnv(env.id)" />
</div>
</div>
</div>
</div>