diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 779e676..431f696 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -64,3 +64,47 @@ jobs: with: files: dist/${{ matrix.name }} generate_release_notes: true + + # ==== 🌟 新增的 DockerHub 全自动推送组 ==== + docker: + # 避免在其他人的野鸡 PR 测试中乱发 + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Checkout codebase + uses: actions/checkout@v4 + + - name: Set up QEMU (支持多架构底层) + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx (全能无缝打包引流引擎) + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract Docker metadata (自动打标签系统) + id: meta + uses: docker/metadata-action@v5 + with: + # 自动组合 "你的名字 / 项目名" + images: ${{ secrets.DOCKERHUB_USERNAME }}/123pan-imagehost + tags: | + type=ref,event=branch + type=ref,event=tag + # 原本的 latest 标签会在 master/main 分支时被自动赋予 + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push to Space (核心推流指令) + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # 借用 Github 的高速缓存,让你后续的打包时间缩短成几秒! + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..50644d1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +# ==== 第一阶段: 编译打包层 ==== +FROM golang:1.21-alpine AS builder + +# 配置国内高速 Go 代理环境,开启跨平台交叉编译所需变量 +ENV GO111MODULE=on \ + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 \ + GOPROXY=https://goproxy.cn,direct + +WORKDIR /app + +# 优先缓存和下载项目依赖 +COPY go.mod go.sum ./ +RUN go mod download + +# 载入完整代码并执行剥离调试信息的极限压缩静态编译 (-ldflags="-w -s") +COPY . . +RUN go build -ldflags="-w -s" -o imagehost ./cmd/main.go + +# ==== 第二阶段: 纯净运行环境层 ==== +FROM alpine:latest + +# 123云盘采用硬 TLS 协议,必须拉取 ca-certificates,否则网络连接拦截报错 +# TZdata 用于纠正容器运行内的时间对齐,保障授权握手不过期 +RUN apk --no-cache add ca-certificates tzdata + +ENV TZ=Asia/Shanghai + +WORKDIR /app + +# 将上一阶段萃取好的精华可执行程序抓取过来 +COPY --from=builder /app/imagehost . + +# 暴露对外的 HTTP 接口 +EXPOSE 8080 + +# 触发点火 +CMD ["./imagehost"] diff --git a/README.md b/README.md index a2f0b46..17b4fb1 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,65 @@ go run cmd/main.go --- +## 🐳 Docker 云原生极简部署 + +如果你使用的是 NAS(如群晖、威联通)、软路由系统或原生携带 Docker 的云服务器,我们极力推荐使用此方案。 + +本项目完全遵循 **云原生十二要素 (12-Factor App)** 规范。全核心参数支持通过 **环境变量 (Environment Variables)** 纯净直连,彻底告别繁杂凌乱的配置文件管理! + +#### 👉 使用 docker-compose 启动 + +找一个空目录,新建 `docker-compose.yml` 文件并粘贴以下内容: + +```yaml +version: '3.8' + +services: + imagehost: + # 替换为你刚才在 DockerHub 推送的独特镜像名! + image: 你的用户名/123pan-imagehost:latest + container_name: 123pan-imagehost + restart: unless-stopped + ports: + - "8080:8080" # 左侧是你要暴露在外网的访问端口 + environment: + # 🌍 全量环保注入,零文件残留! + - PORT=8080 + - CLIENT_ID=请去123pan开放平台申请必填 + - CLIENT_SECRET=请去123pan开放平台申请必填 + - PARENT_FILE_ID= # 专用上传目标的文件夹ID(如果传到根目录则删掉注释留空) + - API_TOKEN=PRIVATE_123_KEY # 拦截门票:强烈建议设为复杂密码防御公网盗刷 + - TZ=Asia/Shanghai +``` + +然后在同一目录下无脑执行: +```bash +docker-compose up -d +``` +启动瞬间,图床将在后台以最高效率纯净运转,一切大功告成! + +*(PS: 对于仍旧偏爱传统物理文件管理配置的老玩家,系统仍包含“智能回落构建”机制:当检测不到上述环境变量时,只要挂载一个空的 `/app/conf` 数据卷进去,系统便会依然乖乖在里面为你生成默认填报模板。)* + +#### 👉 使用原生 Docker 命令行 (纯净一行流) + +如果你连文件都不想建,就喜欢那种直接把代码拍在终端上一键起飞的爽感,可以直接复制并在终端运行以下指令: + +```bash +docker run -d \ + --name 123pan-imagehost \ + --restart unless-stopped \ + -p 8080:8080 \ + -e PORT=8080 \ + -e CLIENT_ID="【去123pan获取并在引流号内填入】" \ + -e CLIENT_SECRET="【去123pan获取并在引号内填入】" \ + -e PARENT_FILE_ID="" \ + -e API_TOKEN="PRIVATE_123_KEY" \ + -e TZ="Asia/Shanghai" \ + 你的用户名/123pan-imagehost:latest +``` + +--- + ## 🛠️ 第三方截图工具接入 (ShareX 等) 本图床严格遵守 `RESTful API` 规范,对第三方工具接入极为友好。以下使用 **ShareX** 为例进行配置: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3026aea --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.8' + +services: + imagehost: + build: . + # 如果你不想编译而是直接拉你刚才推到云端发版的镜像,可以打开下面这行: + # image: sakuradairong/123pan-imagehost:latest + container_name: 123pan-imagehost + restart: unless-stopped + ports: + # 左边是暴露给外部(即你的访问端口),右边是容器内运行端口 + - "8080:8080" + environment: + # 【无文件纯净启动模式】直接把秘钥通过变量投射进去,底层不会再去管 config.yaml 存不存在了! + - PORT=8080 + - CLIENT_ID=这里输入你的客户端ID + - CLIENT_SECRET=这里填你的密钥Secret + - PARENT_FILE_ID=这里留下目标文件夹ID如果不填就放根目录 + - API_TOKEN=PRIVATE_123_KEY # 请千万修改它防御野生网络扫描盗刷 + - TZ=Asia/Shanghai + volumes: + # 如果你还是偏爱通过改动本地物理文件的老式习惯来管理配置,取消下面的注释即可。 + # 此时你可以忽略上面的 environment 变量堆。 + # - ./conf:/app/conf diff --git a/internal/config/config.go b/internal/config/config.go index 4d3bc73..fa131b3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,14 +22,29 @@ var GlobalConfig Config // InitConfig 读取并解析配置文件 func InitConfig(cfgFile string) { - // 【部署容错机制】:判断是否是空白环境运行并进行智能抢险初始化 - if _, err := os.Stat(cfgFile); os.IsNotExist(err) { - log.Printf("⚠️ 尚未检测到配置节点,系统正在初始化并创建框架包至:[%s]", cfgFile) - if err := os.MkdirAll(filepath.Dir(cfgFile), 0755); err != nil { - log.Fatalf("❌ 构建部署文件夹遇到致命阻塞: %v", err) - } + viper.SetConfigFile(cfgFile) + viper.SetConfigType("yaml") - defaultYaml := `# 123pan 私人图床配置文件 + // 映射环境变量并设置默认值防穿透,让用户能够通过 Docker ENV 变量纯净启动! + viper.AutomaticEnv() + viper.SetDefault("port", 8080) + viper.SetDefault("custom_domain", "") + viper.SetDefault("client_id", "") + viper.SetDefault("client_secret", "") + viper.SetDefault("parent_file_id", "") + viper.SetDefault("api_token", "") + + err := viper.ReadInConfig() + + // 【部署容错机制】:当没有配置文件,且!系统环境变量中也没有提取到 client_id 等有效配置时,才触发布署断绝。 + if err != nil && viper.GetString("client_id") == "" { + if _, statErr := os.Stat(cfgFile); os.IsNotExist(statErr) { + log.Printf("⚠️ 尚未检测到物理配置节点及外部环境变量,系统正在初始化框架包至:[%s]", cfgFile) + if err := os.MkdirAll(filepath.Dir(cfgFile), 0755); err != nil { + log.Fatalf("❌ 构建部署文件夹遇到致命阻塞: %v", err) + } + + defaultYaml := `# 123pan 私人图床配置文件 # 服务器端口 port: 8080 @@ -44,23 +59,13 @@ parent_file_id: "" # 您的专属大门密匙!暴露公网请千万修改它防御盗刷 api_token: "PRIVATE_123_KEY" ` - if err := os.WriteFile(cfgFile, []byte(defaultYaml), 0644); err != nil { - log.Fatalf("❌ 框架配置文件剥离解压失败: %v", err) + if err := os.WriteFile(cfgFile, []byte(defaultYaml), 0644); err != nil { + log.Fatalf("❌ 框架配置文件剥离解压失败: %v", err) + } + + // 主动断开并友善通知管理员 + log.Fatalf("✅ 底层初始部署圆满完成!你可以打开刚生成的 %s 填写密钥,或者直接通过在 Docker/系统环境变量中注入 CLIENT_ID 等参数来免密运行!", cfgFile) } - - // 主动断开并友善通知管理员 - log.Fatalf("✅ 系统底层初始部署圆满完成!请打开刚生成的 %s 文件,填入属于你的密钥,然后再重新运行我!", cfgFile) - } - - viper.SetConfigFile(cfgFile) - viper.SetConfigType("yaml") - - // 默认值 - viper.SetDefault("port", 8080) - viper.SetDefault("custom_domain", "") - - if err := viper.ReadInConfig(); err != nil { - log.Fatalf("配置文件读取失败: %v", err) } if err := viper.Unmarshal(&GlobalConfig); err != nil {