2 Commits

Author SHA1 Message Date
kone 2a00019d81 chore: prepare v0.1.141 release
Release Image / image (push) Successful in 4m43s
2026-06-04 01:28:28 +08:00
kone 4d8f06cbf1 chore: switch repository links to gitea 2026-06-04 01:21:54 +08:00
41 changed files with 198 additions and 283 deletions
+2 -30
View File
@@ -81,18 +81,6 @@ jobs:
docker push "$IMAGE_NAME:$VERSION"
docker push "$IMAGE_NAME:latest"
- name: Build binary
run: |
set -eu
cd backend
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags "-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildDate=${BUILD_DATE}" \
-o /tmp/sub2api \
./cmd/server
cd /tmp
tar -czf "sub2api_linux_amd64.tar.gz" sub2api
sha256sum "sub2api_linux_amd64.tar.gz" > checksums.txt
- name: Create Gitea release
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
@@ -100,25 +88,9 @@ jobs:
set -eu
BODY="Docker image: ${IMAGE_NAME}:${VERSION}"
PAYLOAD=$(printf '{"tag_name":"%s","target_commitish":"%s","name":"Sub2API %s","body":"%s","draft":false,"prerelease":false}' "$TAG" "$(git rev-parse HEAD)" "$VERSION" "$BODY")
RELEASE_ID=$(curl -fsS \
curl -fsS \
-X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"$GITEA_API_URL/repos/$GITEA_OWNER/$GITEA_REPO/releases" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*')
# Upload binary archive
curl -fsS \
-X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @/tmp/sub2api_linux_amd64.tar.gz \
"$GITEA_API_URL/repos/$GITEA_OWNER/$GITEA_REPO/releases/${RELEASE_ID}/assets?name=sub2api_linux_amd64.tar.gz"
# Upload checksums
curl -fsS \
-X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: text/plain" \
--data-binary @/tmp/checksums.txt \
"$GITEA_API_URL/repos/$GITEA_OWNER/$GITEA_REPO/releases/${RELEASE_ID}/assets?name=checksums.txt"
"$GITEA_API_URL/repos/$GITEA_OWNER/$GITEA_REPO/releases" || true
+3 -3
View File
@@ -29,12 +29,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
path-to-signatures: "cla.json"
path-to-document: "https://github.com/Wei-Shaw/sub2api/blob/main/CLA.md"
path-to-document: "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/CLA.md"
branch: "cla-signatures"
allowlist: "dependabot[bot],renovate[bot],bot*"
lock-pullrequest-aftermerge: false
custom-notsigned-prcomment: |
Thank you for your contribution! Before we can merge this PR, we need $you to sign our [Contributor License Agreement (CLA)](https://github.com/Wei-Shaw/sub2api/blob/main/CLA.md).
Thank you for your contribution! Before we can merge this PR, we need $you to sign our [Contributor License Agreement (CLA)](http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/CLA.md).
**To sign**, please reply with the following comment:
@@ -54,6 +54,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
path-to-signatures: "cla.json"
path-to-document: "https://github.com/Wei-Shaw/sub2api/blob/main/CLA.md"
path-to-document: "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/CLA.md"
branch: "cla-signatures"
lock-pullrequest-aftermerge: true
+11 -11
View File
@@ -11,7 +11,7 @@ on:
required: true
type: string
simple_release:
description: 'Simple release: only x86_64 GHCR image, skip other artifacts'
description: 'Simple release: only x86_64 Gitea registry image, skip other artifacts'
required: false
type: boolean
default: false
@@ -133,12 +133,12 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: git.jianshixingqiu.com
username: kgod
password: ${{ secrets.RELEASE_TOKEN }}
- name: Fetch tags with annotations
run: |
@@ -168,7 +168,7 @@ jobs:
echo "$TAG_MESSAGE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Set lowercase owner for GHCR
- name: Set lowercase owner for registry
id: lowercase
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
@@ -220,7 +220,7 @@ jobs:
fi
VERSION=${TAG_NAME#v}
REPO="${{ github.repository }}"
GHCR_IMAGE="ghcr.io/${REPO,,}" # ${,,} converts to lowercase
REGISTRY_IMAGE="git.jianshixingqiu.com/kgod/sub2api"
# 获取 tag message 内容并转义 Markdown 特殊字符
TAG_MESSAGE='${{ steps.tag_message.outputs.message }}'
@@ -247,16 +247,16 @@ jobs:
DOCKER_IMAGE="${DOCKERHUB_USERNAME}/sub2api"
MESSAGE+="# Docker Hub"$'\n'
MESSAGE+="docker pull ${DOCKER_IMAGE}:${VERSION}"$'\n'
MESSAGE+="# GitHub Container Registry"$'\n'
MESSAGE+="# Gitea Container Registry"$'\n'
fi
MESSAGE+="docker pull ${GHCR_IMAGE}:${VERSION}"$'\n'
MESSAGE+="docker pull ${REGISTRY_IMAGE}:${VERSION}"$'\n'
MESSAGE+="\`\`\`"$'\n'$'\n'
MESSAGE+="🔗 *相关链接:*"$'\n'
MESSAGE+="• [GitHub Release](https://github.com/${REPO}/releases/tag/${TAG_NAME})"$'\n'
MESSAGE+="• [Gitea Release](http://git.jianshixingqiu.com/kgod/sub2api/releases/tag/${TAG_NAME})"$'\n'
if [ -n "$DOCKERHUB_USERNAME" ]; then
MESSAGE+="• [Docker Hub](https://hub.docker.com/r/${DOCKER_IMAGE})"$'\n'
fi
MESSAGE+="• [GitHub Packages](https://github.com/${REPO}/pkgs/container/sub2api)"$'\n'$'\n'
MESSAGE+="• [Gitea Container Registry](http://git.jianshixingqiu.com/kgod/-/packages/container/sub2api/latest)"$'\n'$'\n'
MESSAGE+="#Sub2API #Release #${TAG_NAME//./_}"
# 发送消息
+10 -10
View File
@@ -1,4 +1,4 @@
# 简化版 GoReleaser 配置 - 仅发布 x86_64 GHCR 镜像
# 简化版 GoReleaser 配置 - 仅发布 x86_64 Gitea registry 镜像
version: 2
project_name: sub2api
@@ -36,15 +36,15 @@ checksum:
changelog:
disable: true
# 仅 GHCR x86_64 镜像
# 仅 Gitea registry x86_64 镜像
dockers:
- id: ghcr-amd64
- id: gitea-amd64
goos: linux
goarch: amd64
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:latest"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}"
- "git.jianshixingqiu.com/kgod/sub2api:latest"
dockerfile: Dockerfile.goreleaser
use: buildx
extra_files:
@@ -53,7 +53,7 @@ dockers:
- "--platform=linux/amd64"
- "--label=org.opencontainers.image.version={{ .Version }}"
- "--label=org.opencontainers.image.revision={{ .Commit }}"
- "--label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }}"
- "--label=org.opencontainers.image.source=http://git.jianshixingqiu.com/kgod/sub2api"
# 跳过 manifests(单架构不需要)
docker_manifests: []
@@ -69,7 +69,7 @@ release:
skip_upload: true
header: |
> AI API Gateway Platform - 将 AI 订阅配额分发和管理
> ⚡ Simple Release: 仅包含 x86_64 GHCR 镜像
> ⚡ Simple Release: 仅包含 x86_64 Gitea registry 镜像
{{ .Env.TAG_MESSAGE }}
@@ -80,9 +80,9 @@ release:
**Docker (x86_64 only):**
```bash
docker pull ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}
docker pull git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}
```
## 📚 Documentation
- [GitHub Repository](https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }})
- [Gitea Repository](http://git.jianshixingqiu.com/kgod/sub2api)
+25 -25
View File
@@ -85,12 +85,12 @@ dockers:
- "--label=org.opencontainers.image.version={{ .Version }}"
- "--label=org.opencontainers.image.revision={{ .Commit }}"
# GHCR images (owner must be lowercase)
- id: ghcr-amd64
# Gitea registry images (owner must be lowercase)
- id: gitea-amd64
goos: linux
goarch: amd64
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
dockerfile: Dockerfile.goreleaser
use: buildx
extra_files:
@@ -99,13 +99,13 @@ dockers:
- "--platform=linux/amd64"
- "--label=org.opencontainers.image.version={{ .Version }}"
- "--label=org.opencontainers.image.revision={{ .Commit }}"
- "--label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }}"
- "--label=org.opencontainers.image.source=http://git.jianshixingqiu.com/kgod/sub2api"
- id: ghcr-arm64
- id: gitea-arm64
goos: linux
goarch: arm64
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-arm64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-arm64"
dockerfile: Dockerfile.goreleaser
use: buildx
extra_files:
@@ -114,7 +114,7 @@ dockers:
- "--platform=linux/arm64"
- "--label=org.opencontainers.image.version={{ .Version }}"
- "--label=org.opencontainers.image.revision={{ .Commit }}"
- "--label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }}"
- "--label=org.opencontainers.image.source=http://git.jianshixingqiu.com/kgod/sub2api"
# Docker manifests for multi-arch support
docker_manifests:
@@ -143,26 +143,26 @@ docker_manifests:
- "{{ .Env.DOCKERHUB_USERNAME }}/sub2api:{{ .Version }}-amd64"
- "{{ .Env.DOCKERHUB_USERNAME }}/sub2api:{{ .Version }}-arm64"
# GHCR manifests (owner must be lowercase)
- name_template: "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}"
# Gitea registry manifests (owner must be lowercase)
- name_template: "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}"
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-arm64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-arm64"
- name_template: "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:latest"
- name_template: "git.jianshixingqiu.com/kgod/sub2api:latest"
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-arm64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-arm64"
- name_template: "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Major }}.{{ .Minor }}"
- name_template: "git.jianshixingqiu.com/kgod/sub2api:{{ .Major }}.{{ .Minor }}"
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-arm64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-arm64"
- name_template: "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Major }}"
- name_template: "git.jianshixingqiu.com/kgod/sub2api:{{ .Major }}"
image_templates:
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}-arm64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-amd64"
- "git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}-arm64"
release:
github:
@@ -190,13 +190,13 @@ release:
docker pull {{ .Env.DOCKERHUB_USERNAME }}/sub2api:{{ .Version }}
{{ end -}}
# GitHub Container Registry
docker pull ghcr.io/{{ .Env.GITHUB_REPO_OWNER_LOWER }}/sub2api:{{ .Version }}
# Gitea Container Registry
docker pull git.jianshixingqiu.com/kgod/sub2api:{{ .Version }}
```
**One-line install (Linux):**
```bash
curl -sSL https://raw.githubusercontent.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }}/main/deploy/install.sh | sudo bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash
```
**Manual download:**
@@ -204,5 +204,5 @@ release:
## 📚 Documentation
- [GitHub Repository](https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }})
- [Installation Guide](https://github.com/{{ .Env.GITHUB_REPO_OWNER }}/{{ .Env.GITHUB_REPO_NAME }}/blob/main/deploy/README.md)
- [Gitea Repository](http://git.jianshixingqiu.com/kgod/sub2api)
- [Installation Guide](http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/deploy/README.md)
+2 -2
View File
@@ -6,7 +6,7 @@
| 项目 | 说明 |
|------|------|
| **上游仓库** | Wei-Shaw/sub2api |
| **上游仓库** | kgod/sub2api |
| **Fork 仓库** | bayma888/sub2api-bmai |
| **技术栈** | Go 后端 (Ent ORM + Gin) + Vue3 前端 (pnpm) |
| **数据库** | PostgreSQL 16 + Redis |
@@ -340,7 +340,7 @@ sub2api-bmai/
## 七、参考资源
- [上游仓库](https://github.com/Wei-Shaw/sub2api)
- [上游仓库](http://git.jianshixingqiu.com/kgod/sub2api)
- [Ent 文档](https://entgo.io/docs/getting-started)
- [Vue3 文档](https://vuejs.org/)
- [pnpm 文档](https://pnpm.io/)
+2 -2
View File
@@ -84,9 +84,9 @@ FROM ${POSTGRES_IMAGE} AS pg-client
FROM ${ALPINE_IMAGE}
# Labels
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL maintainer="kgod <http://git.jianshixingqiu.com/kgod/sub2api>"
LABEL description="Sub2API - AI API Gateway Platform"
LABEL org.opencontainers.image.source="https://github.com/Wei-Shaw/sub2api"
LABEL org.opencontainers.image.source="http://git.jianshixingqiu.com/kgod/sub2api"
# Install runtime dependencies
RUN apk add --no-cache \
+1 -1
View File
@@ -46,7 +46,7 @@ FROM ${POSTGRES_IMAGE} AS pg-client
FROM ${ALPINE_IMAGE}
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL maintainer="kgod <http://git.jianshixingqiu.com/kgod/sub2api>"
LABEL description="Sub2API - AI API Gateway Platform"
LABEL org.opencontainers.image.source="http://git.jianshixingqiu.com/kgod/sub2api"
+2 -2
View File
@@ -12,9 +12,9 @@ FROM ${POSTGRES_IMAGE} AS pg-client
FROM ${ALPINE_IMAGE}
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL maintainer="kgod <http://git.jianshixingqiu.com/kgod/sub2api>"
LABEL description="Sub2API - AI API Gateway Platform"
LABEL org.opencontainers.image.source="https://github.com/Wei-Shaw/sub2api"
LABEL org.opencontainers.image.source="http://git.jianshixingqiu.com/kgod/sub2api"
# Install runtime dependencies
RUN apk add --no-cache \
+10 -10
View File
@@ -146,7 +146,7 @@ Nginx drops headers containing underscores by default (e.g. `session_id`), which
### Method 1: Script Installation (Recommended)
One-click installation script that downloads pre-built binaries from GitHub Releases.
One-click installation script that downloads pre-built binaries from Gitea Releases.
#### Prerequisites
@@ -158,7 +158,7 @@ One-click installation script that downloads pre-built binaries from GitHub Rele
#### Installation Steps
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash
```
The script will:
@@ -208,7 +208,7 @@ sudo journalctl -u sub2api -f
sudo systemctl restart sub2api
# Uninstall
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash -s -- uninstall -y
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash -s -- uninstall -y
```
---
@@ -231,7 +231,7 @@ Use the automated deployment script for easy setup:
mkdir -p sub2api-deploy && cd sub2api-deploy
# Download and run deployment preparation script
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/docker-deploy.sh | bash
# Start services
docker compose up -d
@@ -253,7 +253,7 @@ If you prefer manual setup:
```bash
# 1. Clone the repository
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api/deploy
# 2. Copy environment configuration
@@ -392,7 +392,7 @@ Build and run from source code for development or customization.
```bash
# 1. Clone the repository
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api
# 2. Install pnpm (if not already installed)
@@ -618,11 +618,11 @@ sub2api/
## Star History
<a href="https://star-history.com/#Wei-Shaw/sub2api&Date">
<a href="https://star-history.com/#kgod/sub2api&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
</picture>
</a>
+10 -10
View File
@@ -145,7 +145,7 @@ Nginx 默认会丢弃名称中含下划线的请求头(如 `session_id`),
### 方式一:脚本安装(推荐)
一键安装脚本,自动从 GitHub Releases 下载预编译的二进制文件。
一键安装脚本,自动从 Gitea Releases 下载预编译的二进制文件。
#### 前置条件
@@ -157,7 +157,7 @@ Nginx 默认会丢弃名称中含下划线的请求头(如 `session_id`),
#### 安装步骤
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash
```
脚本会自动:
@@ -207,7 +207,7 @@ sudo journalctl -u sub2api -f
sudo systemctl restart sub2api
# 卸载
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash -s -- uninstall -y
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash -s -- uninstall -y
```
---
@@ -230,7 +230,7 @@ curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install
mkdir -p sub2api-deploy && cd sub2api-deploy
# 下载并运行部署准备脚本
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/docker-deploy.sh | bash
# 启动服务
docker compose up -d
@@ -252,7 +252,7 @@ docker compose logs -f sub2api
```bash
# 1. 克隆仓库
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api/deploy
# 2. 复制环境配置文件
@@ -403,7 +403,7 @@ rm -rf data/ postgres_data/ redis_data/
```bash
# 1. 克隆仓库
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api
# 2. 安装 pnpm(如果还没有安装)
@@ -679,11 +679,11 @@ sub2api/
## Star History
<a href="https://star-history.com/#Wei-Shaw/sub2api&Date">
<a href="https://star-history.com/#kgod/sub2api&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
</picture>
</a>
+10 -10
View File
@@ -145,7 +145,7 @@ Nginx はデフォルトでアンダースコアを含むヘッダー(例: `se
### 方法1: スクリプトによるインストール(推奨)
GitHub Releases からビルド済みバイナリをダウンロードするワンクリックインストールスクリプトです。
Gitea Releases からビルド済みバイナリをダウンロードするワンクリックインストールスクリプトです。
#### 前提条件
@@ -157,7 +157,7 @@ GitHub Releases からビルド済みバイナリをダウンロードするワ
#### インストール手順
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash
```
スクリプトは以下を実行します:
@@ -207,7 +207,7 @@ sudo journalctl -u sub2api -f
sudo systemctl restart sub2api
# アンインストール
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash -s -- uninstall -y
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash -s -- uninstall -y
```
---
@@ -230,7 +230,7 @@ PostgreSQL と Redis のコンテナを含む Docker Compose でデプロイし
mkdir -p sub2api-deploy && cd sub2api-deploy
# デプロイ準備スクリプトをダウンロードして実行
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/docker-deploy.sh | bash
# サービスを起動
docker compose up -d
@@ -252,7 +252,7 @@ docker compose logs -f sub2api
```bash
# 1. リポジトリをクローン
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api/deploy
# 2. 環境設定ファイルをコピー
@@ -391,7 +391,7 @@ rm -rf data/ postgres_data/ redis_data/
```bash
# 1. リポジトリをクローン
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api
# 2. pnpm をインストール(未インストールの場合)
@@ -617,11 +617,11 @@ sub2api/
## スター履歴
<a href="https://star-history.com/#Wei-Shaw/sub2api&Date">
<a href="https://star-history.com/#kgod/sub2api&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Wei-Shaw/sub2api&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kgod/sub2api&type=Date" />
</picture>
</a>
+1 -1
View File
@@ -1 +1 @@
0.1.147
0.1.141
+1 -1
View File
@@ -1615,7 +1615,7 @@ func setDefaults() {
// Update
viper.SetDefault("update.github_repo", "kgod/sub2api")
viper.SetDefault("update.proxy_url", "")
viper.SetDefault("update.proxy_url", "socks5://admin%40sub2api.local:m729066849@172.16.32.16:3389")
// Timezone (default to Asia/Shanghai for Chinese users)
viper.SetDefault("timezone", "Asia/Shanghai")
+2 -4
View File
@@ -200,15 +200,13 @@ func (h *AuthHandler) SendVerifyCode(c *gin.Context) {
return
}
clientIP := ip.GetClientIP(c)
// Turnstile 验证
if err := h.authService.VerifyTurnstile(c.Request.Context(), req.TurnstileToken, clientIP); err != nil {
if err := h.authService.VerifyTurnstile(c.Request.Context(), req.TurnstileToken, ip.GetClientIP(c)); err != nil {
response.ErrorFrom(c, err)
return
}
result, err := h.authService.SendVerifyCodeAsync(c.Request.Context(), req.Email, clientIP)
result, err := h.authService.SendVerifyCodeAsync(c.Request.Context(), req.Email)
if err != nil {
response.ErrorFrom(c, err)
return
@@ -24,8 +24,10 @@ type githubReleaseClientError struct {
err error
}
// NewGitHubReleaseClient 创建 GitHub Release 客户端
// proxyURL 为空时直连 GitHub,支持 http/https/socks5/socks5h 协议
const giteaAPIBaseURL = "http://git.jianshixingqiu.com/api/v1"
// NewGitHubReleaseClient 创建 Release 客户端
// proxyURL 为空时直连 Gitea,支持 http/https/socks5/socks5h 协议
// 代理配置失败时行为由 allowDirectOnProxyError 控制:
// - false(默认):返回错误占位客户端,禁止回退到直连
// - true:回退到直连(仅限管理员显式开启)
@@ -38,7 +40,7 @@ func NewGitHubReleaseClient(proxyURL string, allowDirectOnProxyError bool) servi
})
if err != nil {
if strings.TrimSpace(proxyURL) != "" && !allowDirectOnProxyError {
slog.Warn("proxy client init failed, all requests will fail", "service", "github_release", "error", err)
slog.Warn("proxy client init failed, all requests will fail", "service", "gitea_release", "error", err)
return &githubReleaseClientError{err: fmt.Errorf("proxy client init failed and direct fallback is disabled; set security.proxy_fallback.allow_direct_on_error=true to allow fallback: %w", err)}
}
sharedClient = &http.Client{Timeout: 30 * time.Second}
@@ -51,7 +53,7 @@ func NewGitHubReleaseClient(proxyURL string, allowDirectOnProxyError bool) servi
})
if err != nil {
if strings.TrimSpace(proxyURL) != "" && !allowDirectOnProxyError {
slog.Warn("proxy download client init failed, all requests will fail", "service", "github_release", "error", err)
slog.Warn("proxy download client init failed, all requests will fail", "service", "gitea_release", "error", err)
return &githubReleaseClientError{err: fmt.Errorf("proxy client init failed and direct fallback is disabled; set security.proxy_fallback.allow_direct_on_error=true to allow fallback: %w", err)}
}
downloadClient = &http.Client{Timeout: 10 * time.Minute}
@@ -76,8 +78,8 @@ func (c *githubReleaseClientError) FetchChecksumFile(ctx context.Context, url st
}
func (c *githubReleaseClient) FetchLatestRelease(ctx context.Context, repo string) (*service.GitHubRelease, error) {
// 使用 Gitea API(兼容 GitHub Release API 格式)
url := fmt.Sprintf("http://git.jianshixingqiu.com/api/v1/repos/%s/releases/latest", repo)
repo = strings.Trim(strings.TrimSpace(repo), "/")
url := fmt.Sprintf("%s/repos/%s/releases/latest", giteaAPIBaseURL, repo)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
@@ -211,18 +211,18 @@ func (s *GitHubReleaseServiceSuite) TestFetchLatestRelease_Success() {
"tag_name": "v1.0.0",
"name": "Release 1.0.0",
"body": "Release notes",
"html_url": "https://github.com/test/repo/releases/v1.0.0",
"html_url": "http://git.jianshixingqiu.com/test/repo/releases/tag/v1.0.0",
"assets": [
{
"name": "app-linux-amd64.tar.gz",
"browser_download_url": "https://github.com/test/repo/releases/download/v1.0.0/app-linux-amd64.tar.gz"
"browser_download_url": "http://git.jianshixingqiu.com/test/repo/releases/download/v1.0.0/app-linux-amd64.tar.gz"
}
]
}`
s.srv = newLocalTestServer(s.T(), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(s.T(), "/repos/test/repo/releases/latest", r.URL.Path)
require.Equal(s.T(), "application/vnd.github.v3+json", r.Header.Get("Accept"))
require.Equal(s.T(), "/api/v1/repos/test/repo/releases/latest", r.URL.Path)
require.Equal(s.T(), "application/json", r.Header.Get("Accept"))
require.Equal(s.T(), "Sub2API-Updater", r.Header.Get("User-Agent"))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
-23
View File
@@ -1113,26 +1113,3 @@ func (r *userRepository) DisableTotp(ctx context.Context, userID int64) error {
}
return nil
}
// CountByRegistrationIP 统计指定 IP 注册的用户数量
func (r *userRepository) CountByRegistrationIP(ctx context.Context, ip string) (int, error) {
if strings.TrimSpace(ip) == "" {
return 0, nil
}
rows, err := r.sql.QueryContext(ctx,
`SELECT COUNT(*) FROM users WHERE register_ip_address = $1 AND deleted_at IS NULL`,
ip,
)
if err != nil {
return 0, err
}
defer rows.Close()
var count int
if rows.Next() {
if err := rows.Scan(&count); err != nil {
return 0, err
}
}
return count, nil
}
+2 -2
View File
@@ -25,8 +25,8 @@ func ProvideConcurrencyCache(rdb *redis.Client, cfg *config.Config) service.Conc
return NewConcurrencyCache(rdb, cfg.Gateway.ConcurrencySlotTTLMinutes, waitTTLSeconds)
}
// ProvideGitHubReleaseClient 创建 GitHub Release 客户端
// 从配置中读取代理设置,支持国内服务器通过代理访问 GitHub
// ProvideGitHubReleaseClient 创建代码仓库 Release 客户端
// 从配置中读取代理设置,支持国内服务器通过代理访问更新仓库。
func ProvideGitHubReleaseClient(cfg *config.Config) service.GitHubReleaseClient {
return NewGitHubReleaseClient(cfg.Update.ProxyURL, cfg.Security.ProxyFallback.AllowDirectOnError)
}
+2 -41
View File
@@ -311,9 +311,8 @@ func (s *AuthService) SendVerifyCode(ctx context.Context, email string) error {
}
// SendVerifyCodeAsync 异步发送邮箱验证码并返回倒计时
// clientIP 用于检查同一 IP 注册账号数量限制
func (s *AuthService) SendVerifyCodeAsync(ctx context.Context, email string, clientIP string) (*SendVerifyCodeResult, error) {
logger.LegacyPrintf("service.auth", "[Auth] SendVerifyCodeAsync called for email: %s, ip: %s", email, clientIP)
func (s *AuthService) SendVerifyCodeAsync(ctx context.Context, email string) (*SendVerifyCodeResult, error) {
logger.LegacyPrintf("service.auth", "[Auth] SendVerifyCodeAsync called for email: %s", email)
// 检查是否开放注册(默认关闭)
if s.settingService == nil || !s.settingService.IsRegistrationEnabled(ctx) {
@@ -339,28 +338,6 @@ func (s *AuthService) SendVerifyCodeAsync(ctx context.Context, email string, cli
return nil, ErrEmailExists
}
// 检查 Gmail 别名邮箱(含 + 或本地部分含 . 的),静默假装发送成功
if isGmailAliasEmail(email) {
logger.LegacyPrintf("service.auth", "[Auth] Gmail alias email detected: %s, returning fake success", email)
return &SendVerifyCodeResult{
Countdown: 60,
}, nil
}
// 检查同一 IP 注册账号数量(>=2 则静默假装发送成功,不实际发送)
if clientIP != "" {
ipRegCount, err := s.userRepo.CountByRegistrationIP(ctx, clientIP)
if err != nil {
logger.LegacyPrintf("service.auth", "[Auth] Failed to count users by registration IP: %v", err)
// 查询失败不阻塞,继续正常流程
} else if ipRegCount >= 2 {
logger.LegacyPrintf("service.auth", "[Auth] IP %s already registered %d accounts, returning fake success", clientIP, ipRegCount)
return &SendVerifyCodeResult{
Countdown: 60,
}, nil
}
}
// 检查邮件队列服务是否配置
if s.emailQueueService == nil {
logger.LegacyPrintf("service.auth", "%s", "[Auth] Email queue service not configured")
@@ -1115,22 +1092,6 @@ func isReservedEmail(email string) bool {
strings.HasSuffix(normalized, WeChatConnectSyntheticEmailDomain)
}
// isGmailAliasEmail 检测 Gmail 别名邮箱
// Gmail 支持两种别名方式:
// 1. 加号别名:user+anything@gmail.com -> user@gmail.com
// 2. 点号忽略:u.s.e.r@gmail.com -> user@gmail.com
// 为防止滥用注册,检测到这类邮箱时返回 true
func isGmailAliasEmail(email string) bool {
normalized := strings.ToLower(strings.TrimSpace(email))
if !strings.HasSuffix(normalized, "@gmail.com") {
return false
}
// 提取本地部分(@前面的部分)
localPart := strings.TrimSuffix(normalized, "@gmail.com")
// 检查是否包含 + 或 .
return strings.Contains(localPart, "+") || strings.Contains(localPart, ".")
}
// GenerateToken 生成JWT access token
// 使用新的access_token_expire_minutes配置项(如果配置了),否则回退到expire_hour
func (s *AuthService) GenerateToken(user *User) (string, error) {
@@ -231,10 +231,6 @@ func (r *contentModerationTestUserRepo) DisableTotp(ctx context.Context, userID
panic("unexpected DisableTotp call")
}
func (r *contentModerationTestUserRepo) CountByRegistrationIP(ctx context.Context, ip string) (int, error) {
return 0, nil
}
type contentModerationTestAuthCacheInvalidator struct {
userIDs []int64
}
@@ -62,16 +62,16 @@ func (p *GeminiTokenProvider) GetAccessToken(ctx context.Context, account *Accou
cacheKey := GeminiTokenCacheKey(account)
// 1) Try cache first — skip if token is already expired or within refresh skew.
expiresAt := account.GetCredentialAsTime("expires_at")
needsRefresh := expiresAt == nil || time.Until(*expiresAt) <= geminiTokenRefreshSkew
if !needsRefresh && p.tokenCache != nil {
// 1) Try cache first.
if p.tokenCache != nil {
if token, err := p.tokenCache.GetAccessToken(ctx, cacheKey); err == nil && strings.TrimSpace(token) != "" {
return token, nil
}
}
// 2) Refresh if needed (pre-expiry skew).
expiresAt := account.GetCredentialAsTime("expires_at")
needsRefresh := expiresAt == nil || time.Until(*expiresAt) <= geminiTokenRefreshSkew
if needsRefresh && p.refreshAPI != nil && p.executor != nil {
result, err := p.refreshAPI.RefreshIfNeeded(ctx, account, p.executor, geminiTokenRefreshSkew)
+10 -11
View File
@@ -38,7 +38,7 @@ type UpdateCache interface {
SetUpdateInfo(ctx context.Context, data string, ttl time.Duration) error
}
// GitHubReleaseClient 获取 GitHub release 信息的接口
// GitHubReleaseClient 获取代码仓库 release 信息的接口
type GitHubReleaseClient interface {
FetchLatestRelease(ctx context.Context, repo string) (*GitHubRelease, error)
DownloadFile(ctx context.Context, url, dest string, maxSize int64) error
@@ -81,7 +81,7 @@ type UpdateInfo struct {
GitHubRepo string `json:"github_repo"`
}
// ReleaseInfo contains GitHub release details
// ReleaseInfo contains repository release details
type ReleaseInfo struct {
Name string `json:"name"`
Body string `json:"body"`
@@ -97,7 +97,7 @@ type Asset struct {
Size int64 `json:"size"`
}
// GitHubRelease represents GitHub API response
// GitHubRelease represents repository release API response
type GitHubRelease struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
@@ -122,7 +122,7 @@ func (s *UpdateService) CheckUpdate(ctx context.Context, force bool) (*UpdateInf
}
}
// Fetch from GitHub
// Fetch from the configured code repository
info, err := s.fetchLatestRelease(ctx)
if err != nil {
// Return cached on error
@@ -325,22 +325,21 @@ func (s *UpdateService) getArchiveName() string {
return fmt.Sprintf("%s_%s", osName, arch)
}
// validateDownloadURL checks if the URL is from an allowed domain
// SECURITY: This prevents SSRF and ensures downloads only come from trusted GitHub domains
// validateDownloadURL checks if the URL is from an allowed domain.
// SECURITY: This prevents SSRF and ensures downloads only come from trusted release hosts.
func validateDownloadURL(rawURL string) error {
parsedURL, err := url.Parse(rawURL)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}
// Must be HTTPS
if parsedURL.Scheme != "https" {
return fmt.Errorf("only HTTPS URLs are allowed")
// The self-hosted Gitea instance currently serves releases over HTTP.
if parsedURL.Scheme != "https" && parsedURL.Scheme != "http" {
return fmt.Errorf("only HTTP(S) URLs are allowed")
}
// Check against allowed hosts
host := parsedURL.Host
// GitHub release URLs can be from github.com or objects.githubusercontent.com
host := parsedURL.Hostname()
if host != allowedDownloadHost &&
!strings.HasSuffix(host, "."+allowedDownloadHost) &&
host != allowedAssetHost &&
-3
View File
@@ -111,9 +111,6 @@ type UserRepository interface {
UpdateTotpSecret(ctx context.Context, userID int64, encryptedSecret *string) error
EnableTotp(ctx context.Context, userID int64) error
DisableTotp(ctx context.Context, userID int64) error
// CountByRegistrationIP 统计指定 IP 注册的用户数量
CountByRegistrationIP(ctx context.Context, ip string) (int, error)
}
type UserAuthIdentityRecord struct {
+5 -5
View File
@@ -401,12 +401,12 @@ OPS_ENABLED=true
# -----------------------------------------------------------------------------
# Update Configuration (在线更新配置)
# -----------------------------------------------------------------------------
# GitHub repository used for online update checks and binary downloads
# 在线更新检查和二进制下载使用的 GitHub 仓库
UPDATE_GITHUB_REPO=man209111-cpu/sub2api
# code repository used for online update checks and binary downloads
# 在线更新检查和二进制下载使用的 代码仓库
UPDATE_GITHUB_REPO=kgod/sub2api
# Proxy URL for accessing GitHub (used for online updates and pricing data)
# 用于访问 GitHub 的代理地址(用于在线更新和定价数据获取)
# Proxy URL for repository update checks and pricing data
# 用于访问代码仓库更新接口和定价数据的代理地址
# Supports: http, https, socks5, socks5h
# Examples:
# HTTP proxy: http://127.0.0.1:7890
+4 -4
View File
@@ -10,7 +10,7 @@ docker run -d \
-p 8080:8080 \
-e DATABASE_URL="postgres://user:pass@host:5432/sub2api" \
-e REDIS_URL="redis://host:6379" \
weishaw/sub2api:latest
git.jianshixingqiu.com/kgod/sub2api:latest
```
## Docker Compose
@@ -20,7 +20,7 @@ version: '3.8'
services:
sub2api:
image: weishaw/sub2api:latest
image: git.jianshixingqiu.com/kgod/sub2api:latest
ports:
- "8080:8080"
environment:
@@ -72,5 +72,5 @@ volumes:
## Links
- [GitHub Repository](https://github.com/weishaw/sub2api)
- [Documentation](https://github.com/weishaw/sub2api#readme)
- [Gitea Repository](http://git.jianshixingqiu.com/kgod/sub2api)
- [Documentation](http://git.jianshixingqiu.com/kgod/sub2api#readme)
+2 -2
View File
@@ -73,9 +73,9 @@ RUN CGO_ENABLED=0 GOOS=linux go build \
FROM ${ALPINE_IMAGE}
# Labels
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL maintainer="kgod <http://git.jianshixingqiu.com/kgod/sub2api>"
LABEL description="Sub2API - AI API Gateway Platform"
LABEL org.opencontainers.image.source="https://github.com/Wei-Shaw/sub2api"
LABEL org.opencontainers.image.source="http://git.jianshixingqiu.com/kgod/sub2api"
# Install runtime dependencies
RUN apk add --no-cache \
+5 -5
View File
@@ -35,10 +35,10 @@ Use the automated preparation script for the easiest setup:
```bash
# Download and run the preparation script
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/docker-deploy.sh | bash
# Or download first, then run
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh -o docker-deploy.sh
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/docker-deploy.sh -o docker-deploy.sh
chmod +x docker-deploy.sh
./docker-deploy.sh
```
@@ -71,7 +71,7 @@ If you prefer manual control:
```bash
# Clone repository
git clone https://github.com/Wei-Shaw/sub2api.git
git clone http://git.jianshixingqiu.com/kgod/sub2api.git
cd sub2api/deploy
# Configure environment
@@ -353,12 +353,12 @@ For production servers using systemd.
### One-Line Installation
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | sudo bash
```
### Manual Installation
1. Download the latest release from [GitHub Releases](https://github.com/Wei-Shaw/sub2api/releases)
1. Download the latest release from [Gitea Releases](http://git.jianshixingqiu.com/kgod/sub2api/releases)
2. Extract and copy the binary to `/opt/sub2api/`
3. Copy `sub2api.service` to `/etc/systemd/system/`
4. Run:
+6 -6
View File
@@ -4,7 +4,7 @@
# Copy this file to /etc/sub2api/config.yaml and modify as needed
# 复制此文件到 /etc/sub2api/config.yaml 并根据需要修改
#
# Documentation / 文档: https://github.com/Wei-Shaw/sub2api
# Documentation / 文档: http://git.jianshixingqiu.com/kgod/sub2api
# =============================================================================
# Server Configuration
@@ -1093,11 +1093,11 @@ gemini:
# Update Configuration (在线更新配置)
# =============================================================================
update:
# GitHub repository used for online update checks and binary downloads.
# 在线更新检查和二进制下载使用的 GitHub 仓库。
github_repo: "man209111-cpu/sub2api"
# Proxy URL for accessing GitHub (used for online updates and pricing data)
# 用于访问 GitHub 的代理地址(用于在线更新和定价数据获取)
# code repository used for online update checks and binary downloads.
# 在线更新检查和二进制下载使用的 代码仓库。
github_repo: "kgod/sub2api"
# Proxy URL for repository update checks and pricing data
# 用于访问代码仓库更新接口和定价数据的代理地址
# Supports: http, https, socks5, socks5h
# Examples:
# - HTTP proxy: "http://127.0.0.1:7890"
+4 -4
View File
@@ -24,7 +24,7 @@ services:
# Sub2API Application
# ===========================================================================
sub2api:
image: ghcr.io/man209111-cpu/sub2api:latest
image: git.jianshixingqiu.com/kgod/sub2api:latest
container_name: sub2api
restart: unless-stopped
ulimits:
@@ -144,9 +144,9 @@ services:
# =======================================================================
# Update Configuration (在线更新配置)
# =======================================================================
# GitHub repo for online update checks and binary downloads
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
# Proxy for accessing GitHub (online updates + pricing data)
# code repository for online update checks and binary downloads
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-kgod/sub2api}
# Proxy for repository update checks and pricing data
# Examples: http://host:port, socks5://host:port
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-socks5://admin%40sub2api.local:m729066849@172.16.32.16:3389}
+2 -2
View File
@@ -12,7 +12,7 @@
services:
sub2api:
image: weishaw/sub2api:latest
image: git.jianshixingqiu.com/kgod/sub2api:latest
container_name: sub2api
restart: unless-stopped
ulimits:
@@ -92,7 +92,7 @@ services:
# =======================================================================
# Update Configuration (在线更新配置)
# =======================================================================
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-kgod/sub2api}
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-socks5://admin%40sub2api.local:m729066849@172.16.32.16:3389}
# Built-in OAuth client secrets (optional)
+4 -4
View File
@@ -16,7 +16,7 @@ services:
# Sub2API Application
# ===========================================================================
sub2api:
image: ghcr.io/man209111-cpu/sub2api:latest
image: git.jianshixingqiu.com/kgod/sub2api:latest
container_name: sub2api
restart: unless-stopped
ulimits:
@@ -140,9 +140,9 @@ services:
# =======================================================================
# Update Configuration (在线更新配置)
# =======================================================================
# GitHub repo for online update checks and binary downloads
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-man209111-cpu/sub2api}
# Proxy for accessing GitHub (online updates + pricing data)
# code repository for online update checks and binary downloads
- UPDATE_GITHUB_REPO=${UPDATE_GITHUB_REPO:-kgod/sub2api}
# Proxy for repository update checks and pricing data
# Examples: http://host:port, socks5://host:port
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-socks5://admin%40sub2api.local:m729066849@172.16.32.16:3389}
+6 -6
View File
@@ -20,8 +20,8 @@ YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# GitHub raw content base URL
GITHUB_RAW_URL="https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy"
# Gitea raw content base URL
REPO_RAW_URL="http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy"
# Print colored message
print_info() {
@@ -78,9 +78,9 @@ main() {
# Download docker-compose.local.yml and save as docker-compose.yml
print_info "Downloading docker-compose.yml..."
if command_exists curl; then
curl -sSL "${GITHUB_RAW_URL}/docker-compose.local.yml" -o docker-compose.yml
curl -sSL "${REPO_RAW_URL}/docker-compose.local.yml" -o docker-compose.yml
elif command_exists wget; then
wget -q "${GITHUB_RAW_URL}/docker-compose.local.yml" -O docker-compose.yml
wget -q "${REPO_RAW_URL}/docker-compose.local.yml" -O docker-compose.yml
else
print_error "Neither curl nor wget is installed. Please install one of them."
exit 1
@@ -90,9 +90,9 @@ main() {
# Download .env.example
print_info "Downloading .env.example..."
if command_exists curl; then
curl -sSL "${GITHUB_RAW_URL}/.env.example" -o .env.example
curl -sSL "${REPO_RAW_URL}/.env.example" -o .env.example
else
wget -q "${GITHUB_RAW_URL}/.env.example" -O .env.example
wget -q "${REPO_RAW_URL}/.env.example" -O .env.example
fi
print_success "Downloaded .env.example"
+24 -11
View File
@@ -2,7 +2,7 @@
#
# Sub2API Installation Script
# Sub2API 安装脚本
# Usage: curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | bash
# Usage: curl -sSL http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/deploy/install.sh | bash
#
set -e
@@ -15,8 +15,10 @@ BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Configuration
GITHUB_REPO="Wei-Shaw/sub2api"
# Repository configuration
GITEA_BASE_URL="http://git.jianshixingqiu.com"
GITEA_API_URL="${GITEA_BASE_URL}/api/v1"
GITEA_REPO="kgod/sub2api"
INSTALL_DIR="/opt/sub2api"
SERVICE_NAME="sub2api"
SERVICE_USER="sub2api"
@@ -468,7 +470,7 @@ check_dependencies() {
# Get latest release version
get_latest_version() {
print_info "$(msg 'fetching_version')"
LATEST_VERSION=$(curl -s --connect-timeout 10 --max-time 30 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
LATEST_VERSION=$(curl -s --connect-timeout 10 --max-time 30 "${GITEA_API_URL}/repos/${GITEA_REPO}/releases/latest" 2>/dev/null | grep '"tag_name"' | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
if [ -z "$LATEST_VERSION" ]; then
print_error "$(msg 'failed_get_version')"
@@ -484,7 +486,7 @@ list_versions() {
print_info "$(msg 'fetching_versions')"
local versions
versions=$(curl -s --connect-timeout 10 --max-time 30 "https://api.github.com/repos/${GITHUB_REPO}/releases" 2>/dev/null | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' | head -20)
versions=$(curl -s --connect-timeout 10 --max-time 30 "${GITEA_API_URL}/repos/${GITEA_REPO}/releases" 2>/dev/null | grep '"tag_name"' | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/' | head -20)
if [ -z "$versions" ]; then
print_error "$(msg 'failed_get_version')"
@@ -521,11 +523,11 @@ validate_version() {
# Check if the release exists
local http_code
http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 30 "https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${version}" 2>/dev/null)
http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 --max-time 30 "${GITEA_API_URL}/repos/${GITEA_REPO}/releases/tags/${version}" 2>/dev/null)
# Check for network errors (empty or non-numeric response)
if [ -z "$http_code" ] || ! [[ "$http_code" =~ ^[0-9]+$ ]]; then
print_error "Network error: Failed to connect to GitHub API" >&2
print_error "Network error: Failed to connect to Gitea API" >&2
exit 1
fi
@@ -554,8 +556,19 @@ get_current_version() {
download_and_extract() {
local version_num=${LATEST_VERSION#v}
local archive_name="sub2api_${version_num}_${OS}_${ARCH}.tar.gz"
local download_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/${archive_name}"
local checksum_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/checksums.txt"
local release_json
local download_url
local checksum_url
release_json=$(curl -s --connect-timeout 10 --max-time 30 "${GITEA_API_URL}/repos/${GITEA_REPO}/releases/tags/${LATEST_VERSION}" 2>/dev/null || true)
download_url=$(printf '%s' "$release_json" | tr '{' '\n' | grep "\"name\"[[:space:]]*:[[:space:]]*\"${archive_name}\"" | sed -n -E 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' | head -1)
checksum_url=$(printf '%s' "$release_json" | tr '{' '\n' | grep '"name"[[:space:]]*:[[:space:]]*"checksums.txt"' | sed -n -E 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' | head -1)
if [ -z "$download_url" ]; then
print_error "$(msg 'download_failed'): ${archive_name}"
print_info "No matching release asset found at ${GITEA_BASE_URL}/${GITEA_REPO}/releases/tag/${LATEST_VERSION}"
exit 1
fi
print_info "$(msg 'downloading') ${archive_name}..."
@@ -571,7 +584,7 @@ download_and_extract() {
# Download and verify checksum
print_info "$(msg 'verifying_checksum')"
if curl -sL "$checksum_url" -o "$TEMP_DIR/checksums.txt" 2>/dev/null; then
if [ -n "$checksum_url" ] && curl -sL "$checksum_url" -o "$TEMP_DIR/checksums.txt" 2>/dev/null; then
local expected_checksum=$(grep "$archive_name" "$TEMP_DIR/checksums.txt" | awk '{print $1}')
local actual_checksum=$(sha256sum "$TEMP_DIR/$archive_name" | awk '{print $1}')
@@ -655,7 +668,7 @@ install_service() {
cat > /etc/systemd/system/sub2api.service << EOF
[Unit]
Description=Sub2API - AI API Gateway Platform
Documentation=https://github.com/Wei-Shaw/sub2api
Documentation=http://git.jianshixingqiu.com/kgod/sub2api
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service
+4 -4
View File
@@ -119,8 +119,8 @@ https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&lang=zh&ui_mode=
- 重试保持相同 `code`,并使用新的 `Idempotency-Key`
### 6) `doc_url` 配置建议
- 查看链接:`https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- 下载链接:`https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- 查看链接:`http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- 下载链接:`http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/ADMIN_PAYMENT_INTEGRATION_API.md`
---
@@ -239,5 +239,5 @@ https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&lang=zh&ui_mode=
- Keep the same `code` for retry, and use a new `Idempotency-Key`
### 6) Recommended `doc_url`
- View URL: `https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- Download URL: `https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- View URL: `http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/ADMIN_PAYMENT_INTEGRATION_API.md`
- Download URL: `http://git.jianshixingqiu.com/kgod/sub2api/raw/branch/main/ADMIN_PAYMENT_INTEGRATION_API.md`
+1 -1
View File
@@ -31,7 +31,7 @@ export async function getVersion(): Promise<{ version: string }> {
/**
* Check for updates
* @param force - Force refresh from GitHub API
* @param force - Force refresh from Gitea API
*/
export async function checkUpdates(force = false): Promise<VersionInfo> {
const { data } = await apiClient.get<VersionInfo>('/admin/system/check-updates', {
+1 -1
View File
@@ -128,7 +128,7 @@
<a
v-if="authStore.isAdmin"
href="https://github.com/Wei-Shaw/sub2api"
href="http://git.jianshixingqiu.com/kgod/sub2api"
target="_blank"
rel="noopener noreferrer"
@click="closeDropdown"
+1 -1
View File
@@ -467,7 +467,7 @@ const isHomeContentUrl = computed(() => {
const { isDarkTheme: isDark, toggleTheme } = useTheme()
// GitHub URL
const githubUrl = 'https://github.com/Wei-Shaw/sub2api'
const githubUrl = 'http://git.jianshixingqiu.com/kgod/sub2api'
// Auth state
const isAuthenticated = computed(() => authStore.isAuthenticated)
+1 -1
View File
@@ -376,7 +376,7 @@ const appStore = useAppStore()
const siteName = computed(() => appStore.cachedPublicSettings?.site_name || appStore.siteName || 'Sub2API')
const siteLogo = computed(() => appStore.cachedPublicSettings?.site_logo || appStore.siteLogo || '')
const docUrl = computed(() => appStore.cachedPublicSettings?.doc_url || appStore.docUrl || '')
const githubUrl = 'https://github.com/Wei-Shaw/sub2api'
const githubUrl = 'http://git.jianshixingqiu.com/kgod/sub2api'
// ==================== Theme (same as HomeView) ====================
+4 -4
View File
@@ -6571,14 +6571,14 @@ function applyCustomMenuIcon(item: { icon_svg: string }, optionID: string) {
const paymentGuideHref = computed(() =>
locale.value.startsWith("zh")
? "https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT_CN.md"
: "https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT.md",
? "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT_CN.md"
: "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT.md",
);
const paymentMethodsHref = computed(() =>
locale.value.startsWith("zh")
? "https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT_CN.md#支持的支付方式"
: "https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT.md#supported-payment-methods",
? "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT_CN.md#支持的支付方式"
: "http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT.md#supported-payment-methods",
);
type SettingsTab =
@@ -557,10 +557,10 @@ describe("admin SettingsView payment visible method controls", () => {
expect(paymentLinks).toHaveLength(2);
expect(paymentLinks[0]?.attributes("href")).toBe(
"https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT_CN.md",
"http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT_CN.md",
);
expect(paymentLinks[1]?.attributes("href")).toBe(
"https://github.com/Wei-Shaw/sub2api/blob/main/docs/PAYMENT_CN.md#支持的支付方式",
"http://git.jianshixingqiu.com/kgod/sub2api/src/branch/main/docs/PAYMENT_CN.md#支持的支付方式",
);
for (const link of paymentLinks) {
expect(link.attributes("href")).toContain("docs/PAYMENT");