Compare commits
27 Commits
cd7be35668
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e22a34ef3e | |||
| b8dab70de5 | |||
| d3da56891b | |||
| 3b4429bdd9 | |||
| be4cc669d2 | |||
| 54d4ebd54c | |||
| 20c87d79ea | |||
| b42c90188a | |||
| 9bdc593db4 | |||
| baeb875471 | |||
| f6047c6476 | |||
| a67a889293 | |||
| 1cd83a3099 | |||
| e8c651cc6b | |||
| 24c2cbd0e0 | |||
| 5af84c5628 | |||
| 7750daed54 | |||
| 6363a85505 | |||
| 552bfb0915 | |||
| 18ee00a23f | |||
| 72c11fc762 | |||
| 329a7815b8 | |||
| 2161df8cc7 | |||
| 8d1c16ba04 | |||
| dae845fe74 | |||
| 77ab4e4785 | |||
| fe914e781e |
@@ -0,0 +1,12 @@
|
|||||||
|
# 数据库连接字符串
|
||||||
|
# 请根据您的实际环境修改此处的数据库用户名、密码、地址、端口和数据库名
|
||||||
|
DATABASE_DSN="mysql://root:password@tcp(127.0.0.1:3306)/ppanel?charset=utf8mb4&parseTime=true&multiStatements=true"
|
||||||
|
|
||||||
|
# 应用签名密钥 (App Signature Secrets)
|
||||||
|
# 在 Go-Zero 配置 (e.g., apps/api/etc/api-dev.yaml) 中,
|
||||||
|
# AppSecrets 下的键名 (例如 "android-client", "web-client") 即为 APP_ID。
|
||||||
|
# 对应的环境变量值 (APP_SECRET_ANDROID_CLIENT, APP_SECRET_WEB_CLIENT) 为其 SECRET_KEY。
|
||||||
|
APP_SECRET_ANDROID_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "android-client"
|
||||||
|
APP_SECRET_WEB_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "web-client"
|
||||||
|
APP_SECRET_IOS_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "ios-client"
|
||||||
|
APP_SECRET_MAC_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "mac-client"
|
||||||
+176
-52
@@ -17,8 +17,8 @@ env:
|
|||||||
SSH_PORT: ${{ vars.SSH_PORT }}
|
SSH_PORT: ${{ vars.SSH_PORT }}
|
||||||
SSH_USER: ${{ vars.SSH_USER }}
|
SSH_USER: ${{ vars.SSH_USER }}
|
||||||
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
||||||
TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
|
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
||||||
TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}
|
TG_CHAT_ID: "-4940243803"
|
||||||
VERSION: ${{ github.sha }}
|
VERSION: ${{ github.sha }}
|
||||||
BUILDTIME: ${{ github.event.head_commit.timestamp }}
|
BUILDTIME: ${{ github.event.head_commit.timestamp }}
|
||||||
|
|
||||||
@@ -27,11 +27,20 @@ jobs:
|
|||||||
# Job 1: 设置环境变量,供后续 jobs 共享
|
# Job 1: 设置环境变量,供后续 jobs 共享
|
||||||
# ============================================================
|
# ============================================================
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ario-server
|
runs-on: zero-ppanel-server
|
||||||
|
container:
|
||||||
|
image: node:20
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# 只有node支持版本号别名
|
||||||
|
node: ['20.15.1']
|
||||||
outputs:
|
outputs:
|
||||||
docker_tag: ${{ steps.vars.outputs.docker_tag }}
|
docker_tag: ${{ steps.vars.outputs.docker_tag }}
|
||||||
container_suffix: ${{ steps.vars.outputs.container_suffix }}
|
container_suffix: ${{ steps.vars.outputs.container_suffix }}
|
||||||
deploy_path: ${{ steps.vars.outputs.deploy_path }}
|
deploy_path: ${{ steps.vars.outputs.deploy_path }}
|
||||||
|
has_changes: ${{ steps.changes.outputs.has_changes }}
|
||||||
|
changed_services: ${{ steps.changes.outputs.changed_services }}
|
||||||
|
deploy_services: ${{ steps.changes.outputs.deploy_services }}
|
||||||
steps:
|
steps:
|
||||||
- name: ⚙️ 计算部署变量
|
- name: ⚙️ 计算部署变量
|
||||||
id: vars
|
id: vars
|
||||||
@@ -54,16 +63,127 @@ jobs:
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: 📥 下载代码
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: 🔍 检测变更服务
|
||||||
|
id: changes
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BEFORE="${{ github.event.before }}"
|
||||||
|
SHA="${{ github.sha }}"
|
||||||
|
RANGE=""
|
||||||
|
|
||||||
|
if [ -n "${BEFORE}" ] && [ "${BEFORE}" != "0000000000000000000000000000000000000000" ]; then
|
||||||
|
RANGE="${BEFORE}..${SHA}"
|
||||||
|
elif git rev-parse HEAD~1 >/dev/null 2>&1; then
|
||||||
|
RANGE="HEAD~1..HEAD"
|
||||||
|
else
|
||||||
|
RANGE="HEAD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${RANGE}" = "HEAD" ]; then
|
||||||
|
git show --pretty=format: --name-only HEAD | sed '/^$/d' > changed_files.txt
|
||||||
|
else
|
||||||
|
git diff --name-only "${RANGE}" > changed_files.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Changed files:"
|
||||||
|
cat changed_files.txt || true
|
||||||
|
|
||||||
|
has_file() {
|
||||||
|
grep -Eq "$1" changed_files.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
add_service() {
|
||||||
|
local service="$1"
|
||||||
|
if [[ " ${services[*]} " != *" ${service} "* ]]; then
|
||||||
|
services+=("${service}")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
all=0
|
||||||
|
if has_file '^(go\.mod|go\.sum|pkg/|sql/|deploy/docker-compose\.cloud\.yml|deploy/docker-compose-env\.yml|deploy/\.env\.example|\.gitea/workflows/deploy\.yml)'; then
|
||||||
|
all=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
services=()
|
||||||
|
if [ "${all}" -eq 1 ]; then
|
||||||
|
services=(rpc-core api admin node queue scheduler)
|
||||||
|
else
|
||||||
|
if has_file '^apps/rpc/' || has_file '^deploy/Dockerfile.rpc-core$' || has_file '^deploy/etc/core/'; then
|
||||||
|
add_service "rpc-core"
|
||||||
|
fi
|
||||||
|
if has_file '^apps/api/' || has_file '^deploy/Dockerfile.api$' || has_file '^deploy/etc/api/'; then
|
||||||
|
add_service "api"
|
||||||
|
fi
|
||||||
|
if has_file '^apps/admin/' || has_file '^deploy/Dockerfile.admin$' || has_file '^deploy/etc/admin/'; then
|
||||||
|
add_service "admin"
|
||||||
|
fi
|
||||||
|
if has_file '^apps/node/' || has_file '^deploy/Dockerfile.node$' || has_file '^deploy/etc/node/'; then
|
||||||
|
add_service "node"
|
||||||
|
fi
|
||||||
|
if has_file '^apps/queue/' || has_file '^deploy/Dockerfile.queue$' || has_file '^deploy/etc/queue/'; then
|
||||||
|
add_service "queue"
|
||||||
|
fi
|
||||||
|
if has_file '^apps/scheduler/' || has_file '^deploy/Dockerfile.scheduler$' || has_file '^deploy/etc/scheduler/'; then
|
||||||
|
add_service "scheduler"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${#services[@]}" -eq 0 ]; then
|
||||||
|
echo "No service changes detected, skip build/deploy."
|
||||||
|
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "changed_services=" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "deploy_services=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
deploy_services=()
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
case "${service}" in
|
||||||
|
rpc-core) deploy_services+=("ppanel-rpc-core") ;;
|
||||||
|
api) deploy_services+=("ppanel-api") ;;
|
||||||
|
admin) deploy_services+=("ppanel-admin") ;;
|
||||||
|
node) deploy_services+=("ppanel-node") ;;
|
||||||
|
queue) deploy_services+=("ppanel-queue") ;;
|
||||||
|
scheduler) deploy_services+=("ppanel-scheduler") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
changed_services="$(IFS=,; echo "${services[*]}")"
|
||||||
|
deploy_services_str="${deploy_services[*]}"
|
||||||
|
|
||||||
|
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "changed_services=${changed_services}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "deploy_services=${deploy_services_str}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "Will build services: ${changed_services}"
|
||||||
|
echo "Will deploy services: ${deploy_services_str}"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Job 2: 并行矩阵构建 5 个服务镜像
|
# Job 2: 并行矩阵构建 6 个服务镜像 - 💥 重点修改这里 💥
|
||||||
# ============================================================
|
# ============================================================
|
||||||
build:
|
build:
|
||||||
runs-on: ario-server
|
runs-on: zero-ppanel-server
|
||||||
|
container: # <-- 整个 build job 在 Node.js 容器中运行
|
||||||
|
image: node:20.15.1
|
||||||
|
volumes:
|
||||||
|
- /root/go/pkg/mod:/root/go/pkg/mod
|
||||||
|
- /root/.cache/go-build:/root/.cache/go-build
|
||||||
needs: prepare
|
needs: prepare
|
||||||
|
if: needs.prepare.outputs.has_changes == 'true' && contains(needs.prepare.outputs.changed_services, matrix.service.name)
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
service:
|
service:
|
||||||
|
- name: rpc-core
|
||||||
|
dockerfile: deploy/Dockerfile.rpc-core
|
||||||
|
image_name: ppanel-rpc-core
|
||||||
- name: api
|
- name: api
|
||||||
dockerfile: deploy/Dockerfile.api
|
dockerfile: deploy/Dockerfile.api
|
||||||
image_name: ppanel-api
|
image_name: ppanel-api
|
||||||
@@ -84,65 +204,61 @@ jobs:
|
|||||||
- name: 📥 下载代码
|
- name: 📥 下载代码
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 🔧 安装 Docker CLI
|
- name: Set up Go environment # 在 build job 中也设置 Go 环境
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.24.0' # 确保使用 go.mod 中指定的精确版本
|
||||||
|
|
||||||
|
- name: 🔧 确保 Docker CLI 可用并初始化 Go Modules
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y ca-certificates curl gnupg
|
||||||
|
|
||||||
# 等待 apt 锁释放
|
# !!! 在 node 容器中安装 docker-ce-cli !!!
|
||||||
for i in $(seq 1 60); do
|
|
||||||
if ! fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; then break; fi
|
|
||||||
echo "等待 apt 锁... ($i/60)"; sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
apt-get update -y -o Dpkg::Lock::Timeout=300
|
|
||||||
apt-get install -y -o Dpkg::Lock::Timeout=300 ca-certificates curl gnupg
|
|
||||||
|
|
||||||
# 安装 Docker 官方 CLI (API >= 1.44)
|
|
||||||
install -m 0755 -d /etc/apt/keyrings
|
|
||||||
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
||||||
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
|
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
|
||||||
https://download.docker.com/linux/debian \
|
https://download.docker.com/linux/debian \
|
||||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||||
> /etc/apt/sources.list.d/docker.list
|
> /etc/apt/sources.list.d/docker.list
|
||||||
apt-get update -y -o Dpkg::Lock::Timeout=300
|
apt-get update -y
|
||||||
apt-get install -y -o Dpkg::Lock::Timeout=300 docker-ce-cli docker-buildx-plugin
|
apt-get install -y docker-ce-cli # <<-- 确保 docker CLI 被安装
|
||||||
|
|
||||||
echo "Docker CLI 版本: $(docker --version)"
|
echo "Docker CLI 版本: $(docker --version)"
|
||||||
echo "API 版本: $(docker version --format '{{.Client.APIVersion}}')"
|
echo "Test Docker connectivity: $(docker info --format '{{.ServerVersion}}') (Server)"
|
||||||
|
|
||||||
- name: 📤 构建并推送 ${{ matrix.service.name }}
|
go env -w GOPROXY=https://goproxy.cn,direct # 设置 Go Proxy
|
||||||
|
go mod download # 确保所有模块已下载(缓存命中时自动跳过)
|
||||||
|
|
||||||
|
- name: 📦 构建并推送 Docker 镜像
|
||||||
run: |
|
run: |
|
||||||
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
set -e
|
||||||
IMAGE_BASE="${{ env.REPO }}/${{ matrix.service.image_name }}"
|
IMAGE_NAME="${{ matrix.service.image_name }}"
|
||||||
|
DOCKERFILE="${{ matrix.service.dockerfile }}"
|
||||||
|
DEPLOY_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||||||
|
REPO="${{ env.REPO }}"
|
||||||
|
|
||||||
echo "构建服务: ${{ matrix.service.name }}"
|
FULL_IMAGE_NAME="${REPO}/${IMAGE_NAME}:${DEPLOY_TAG}"
|
||||||
echo "镜像: ${IMAGE_BASE}"
|
|
||||||
echo "标签: ${DOCKER_TAG} | ${{ env.VERSION }}"
|
|
||||||
|
|
||||||
docker build \
|
echo "🚀 开始构建镜像: ${FULL_IMAGE_NAME} 从 ${DOCKERFILE}"
|
||||||
-f ${{ matrix.service.dockerfile }} \
|
docker build -t "${FULL_IMAGE_NAME}" -f "${DOCKERFILE}" .
|
||||||
--platform linux/amd64 \
|
|
||||||
--build-arg VERSION=${{ env.VERSION }} \
|
|
||||||
--build-arg BUILDTIME=${{ env.BUILDTIME }} \
|
|
||||||
-t ${IMAGE_BASE}:${{ env.VERSION }} \
|
|
||||||
-t ${IMAGE_BASE}:${DOCKER_TAG} \
|
|
||||||
.
|
|
||||||
|
|
||||||
docker push ${IMAGE_BASE}:${{ env.VERSION }}
|
echo "⬆️ 推送镜像: ${FULL_IMAGE_NAME}"
|
||||||
docker push ${IMAGE_BASE}:${DOCKER_TAG}
|
docker push "${FULL_IMAGE_NAME}"
|
||||||
|
|
||||||
echo "✅ ${{ matrix.service.name }} 镜像推送完成"
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Job 3: 部署到服务器
|
# Job 3: 部署到服务器
|
||||||
# ============================================================
|
# ============================================================
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ario-server
|
runs-on: zero-ppanel-server
|
||||||
|
container: # <-- 新增:deploy job 也在 Node.js 容器中运行
|
||||||
|
image: node:20.15.1
|
||||||
needs: [prepare, build]
|
needs: [prepare, build]
|
||||||
# PR 不触发部署,只有直接推送才部署
|
# PR 不触发部署,只有直接推送才部署
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push' && needs.prepare.outputs.has_changes == 'true'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 下载代码 (获取 docker-compose.cloud.yml)
|
- name: 📥 下载代码 (获取 docker-compose.cloud.yml)
|
||||||
@@ -155,7 +271,7 @@ jobs:
|
|||||||
username: ${{ env.SSH_USER }}
|
username: ${{ env.SSH_USER }}
|
||||||
password: ${{ env.SSH_PASSWORD }}
|
password: ${{ env.SSH_PASSWORD }}
|
||||||
port: ${{ env.SSH_PORT }}
|
port: ${{ env.SSH_PORT }}
|
||||||
source: "deploy/docker-compose.cloud.yml"
|
source: "deploy/docker-compose.cloud.yml,deploy/etc,deploy/.env.example"
|
||||||
target: "${{ needs.prepare.outputs.deploy_path }}/"
|
target: "${{ needs.prepare.outputs.deploy_path }}/"
|
||||||
strip_components: 1
|
strip_components: 1
|
||||||
|
|
||||||
@@ -173,24 +289,32 @@ jobs:
|
|||||||
DEPLOY_PATH="${{ needs.prepare.outputs.deploy_path }}"
|
DEPLOY_PATH="${{ needs.prepare.outputs.deploy_path }}"
|
||||||
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||||||
REPO="${{ env.REPO }}"
|
REPO="${{ env.REPO }}"
|
||||||
|
DEPLOY_SERVICES="${{ needs.prepare.outputs.deploy_services }}"
|
||||||
|
|
||||||
echo "部署目录: ${DEPLOY_PATH}"
|
echo "部署目录: ${DEPLOY_PATH}"
|
||||||
echo "镜像标签: ${DOCKER_TAG}"
|
echo "镜像标签: ${DOCKER_TAG}"
|
||||||
|
|
||||||
cd ${DEPLOY_PATH}
|
cd ${DEPLOY_PATH}
|
||||||
|
|
||||||
# 写入环境变量供 docker-compose 使用
|
# 保留已有业务配置,仅更新镜像仓库和标签
|
||||||
cat > .env <<EOF
|
touch .env
|
||||||
PPANEL_TAG=${DOCKER_TAG}
|
grep -q '^PPANEL_TAG=' .env \
|
||||||
PPANEL_REPO=${REPO}
|
&& sed -i "s|^PPANEL_TAG=.*|PPANEL_TAG=${DOCKER_TAG}|" .env \
|
||||||
EOF
|
|| echo "PPANEL_TAG=${DOCKER_TAG}" >> .env
|
||||||
|
grep -q '^PPANEL_REPO=' .env \
|
||||||
|
&& sed -i "s|^PPANEL_REPO=.*|PPANEL_REPO=${REPO}|" .env \
|
||||||
|
|| echo "PPANEL_REPO=${REPO}" >> .env
|
||||||
|
|
||||||
|
if [ -z "${DEPLOY_SERVICES}" ]; then
|
||||||
|
echo "没有服务变更,跳过部署。"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# 拉取所有服务的最新镜像
|
# 拉取所有服务的最新镜像
|
||||||
docker-compose -f docker-compose.cloud.yml pull
|
docker-compose -f docker-compose.cloud.yml pull ${DEPLOY_SERVICES}
|
||||||
|
|
||||||
# 滚动更新所有 ppanel 服务
|
# 滚动更新所有 ppanel 服务
|
||||||
docker-compose -f docker-compose.cloud.yml up -d \
|
docker-compose -f docker-compose.cloud.yml up -d ${DEPLOY_SERVICES}
|
||||||
ppanel-api ppanel-admin ppanel-node ppanel-queue ppanel-scheduler
|
|
||||||
|
|
||||||
# 清理旧镜像
|
# 清理旧镜像
|
||||||
docker image prune -f || true
|
docker image prune -f || true
|
||||||
@@ -202,7 +326,7 @@ jobs:
|
|||||||
# Job 4: 通知
|
# Job 4: 通知
|
||||||
# ============================================================
|
# ============================================================
|
||||||
notify:
|
notify:
|
||||||
runs-on: ario-server
|
runs-on: zero-ppanel-server
|
||||||
needs: [prepare, build, deploy]
|
needs: [prepare, build, deploy]
|
||||||
if: always() && github.event_name == 'push'
|
if: always() && github.event_name == 'push'
|
||||||
steps:
|
steps:
|
||||||
@@ -212,7 +336,7 @@ jobs:
|
|||||||
token: ${{ env.TG_BOT_TOKEN }}
|
token: ${{ env.TG_BOT_TOKEN }}
|
||||||
to: ${{ env.TG_CHAT_ID }}
|
to: ${{ env.TG_CHAT_ID }}
|
||||||
message: |
|
message: |
|
||||||
${{ (needs.build.result == 'success' && needs.deploy.result == 'success') && '✅ 部署成功!' || '❌ 部署失败!' }}
|
${{ needs.prepare.outputs.has_changes != 'true' && '⏭️ 无服务变更,已跳过构建与部署。' || ((needs.build.result == 'success' && needs.deploy.result == 'success') && '✅ 部署成功!' || '❌ 部署失败!') }}
|
||||||
|
|
||||||
📦 项目: zero-ppanel
|
📦 项目: zero-ppanel
|
||||||
🌿 分支: ${{ github.ref_name }}
|
🌿 分支: ${{ github.ref_name }}
|
||||||
@@ -221,6 +345,6 @@ jobs:
|
|||||||
👤 提交者: ${{ github.actor }}
|
👤 提交者: ${{ github.actor }}
|
||||||
🕐 时间: ${{ github.event.head_commit.timestamp }}
|
🕐 时间: ${{ github.event.head_commit.timestamp }}
|
||||||
|
|
||||||
构建: ${{ needs.build.result }} | 部署: ${{ needs.deploy.result }}
|
构建: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.build.result }} | 部署: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.deploy.result }}
|
||||||
${{ (needs.build.result != 'success' || needs.deploy.result != 'success') && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
|
${{ (needs.prepare.outputs.has_changes == 'true' && (needs.build.result != 'success' || needs.deploy.result != 'success')) && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
|
||||||
parse_mode: Markdown
|
parse_mode: Markdown
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ logs/
|
|||||||
# Local config overrides
|
# Local config overrides
|
||||||
etc/*.local.yaml
|
etc/*.local.yaml
|
||||||
apps/*/etc/*.local.yaml
|
apps/*/etc/*.local.yaml
|
||||||
|
deploy/.env
|
||||||
|
goctl_tpl/
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/cache
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# the name by which the project can be referenced within Serena
|
||||||
|
project_name: "zero-ppanel"
|
||||||
|
|
||||||
|
|
||||||
|
# list of languages for which language servers are started; choose from:
|
||||||
|
# al bash clojure cpp csharp
|
||||||
|
# csharp_omnisharp dart elixir elm erlang
|
||||||
|
# fortran fsharp go groovy haskell
|
||||||
|
# java julia kotlin lua markdown
|
||||||
|
# matlab nix pascal perl php
|
||||||
|
# php_phpactor powershell python python_jedi r
|
||||||
|
# rego ruby ruby_solargraph rust scala
|
||||||
|
# swift terraform toml typescript typescript_vts
|
||||||
|
# vue yaml zig
|
||||||
|
# (This list may be outdated. For the current list, see values of Language enum here:
|
||||||
|
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
||||||
|
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
||||||
|
# Note:
|
||||||
|
# - For C, use cpp
|
||||||
|
# - For JavaScript, use typescript
|
||||||
|
# - For Free Pascal/Lazarus, use pascal
|
||||||
|
# Special requirements:
|
||||||
|
# Some languages require additional setup/installations.
|
||||||
|
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
||||||
|
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
||||||
|
# The first language is the default language and the respective language server will be used as a fallback.
|
||||||
|
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
||||||
|
languages:
|
||||||
|
- go
|
||||||
|
|
||||||
|
# the encoding used by text files in the project
|
||||||
|
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
||||||
|
encoding: "utf-8"
|
||||||
|
|
||||||
|
# The language backend to use for this project.
|
||||||
|
# If not set, the global setting from serena_config.yml is used.
|
||||||
|
# Valid values: LSP, JetBrains
|
||||||
|
# Note: the backend is fixed at startup. If a project with a different backend
|
||||||
|
# is activated post-init, an error will be returned.
|
||||||
|
language_backend:
|
||||||
|
|
||||||
|
# whether to use project's .gitignore files to ignore files
|
||||||
|
ignore_all_files_in_gitignore: true
|
||||||
|
|
||||||
|
# list of additional paths to ignore in this project.
|
||||||
|
# Same syntax as gitignore, so you can use * and **.
|
||||||
|
# Note: global ignored_paths from serena_config.yml are also applied additively.
|
||||||
|
ignored_paths: []
|
||||||
|
|
||||||
|
# whether the project is in read-only mode
|
||||||
|
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||||
|
# Added on 2025-04-18
|
||||||
|
read_only: false
|
||||||
|
|
||||||
|
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||||
|
# Below is the complete list of tools for convenience.
|
||||||
|
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||||
|
# execute `uv run scripts/print_tool_overview.py`.
|
||||||
|
#
|
||||||
|
# * `activate_project`: Activates a project by name.
|
||||||
|
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||||
|
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||||
|
# * `delete_lines`: Deletes a range of lines within a file.
|
||||||
|
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||||
|
# * `execute_shell_command`: Executes a shell command.
|
||||||
|
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||||
|
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||||
|
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||||
|
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||||
|
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||||
|
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||||
|
# Should only be used in settings where the system prompt cannot be set,
|
||||||
|
# e.g. in clients you have no control over, like Claude Desktop.
|
||||||
|
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||||
|
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||||
|
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||||
|
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||||
|
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||||
|
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||||
|
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||||
|
# * `read_file`: Reads a file within the project directory.
|
||||||
|
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||||
|
# * `remove_project`: Removes a project from the Serena configuration.
|
||||||
|
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||||
|
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||||
|
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||||
|
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||||
|
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||||
|
# * `switch_modes`: Activates modes by providing a list of their names
|
||||||
|
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||||
|
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||||
|
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||||
|
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||||
|
excluded_tools: []
|
||||||
|
|
||||||
|
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
|
||||||
|
included_optional_tools: []
|
||||||
|
|
||||||
|
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
||||||
|
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
||||||
|
fixed_tools: []
|
||||||
|
|
||||||
|
# list of mode names to that are always to be included in the set of active modes
|
||||||
|
# The full set of modes to be activated is base_modes + default_modes.
|
||||||
|
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
|
||||||
|
# Otherwise, this setting overrides the global configuration.
|
||||||
|
# Set this to [] to disable base modes for this project.
|
||||||
|
# Set this to a list of mode names to always include the respective modes for this project.
|
||||||
|
base_modes:
|
||||||
|
|
||||||
|
# list of mode names that are to be activated by default.
|
||||||
|
# The full set of modes to be activated is base_modes + default_modes.
|
||||||
|
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
|
||||||
|
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
||||||
|
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
||||||
|
default_modes:
|
||||||
|
|
||||||
|
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||||
|
# (contrary to the memories, which are loaded on demand).
|
||||||
|
initial_prompt: ""
|
||||||
|
|
||||||
|
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
||||||
|
# such as docstrings or parameter information.
|
||||||
|
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
||||||
|
# If null or missing, use the setting from the global configuration.
|
||||||
|
symbol_info_budget:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
.PHONY: build-all build-api build-admin build-node build-queue build-scheduler build-rpc-core \
|
.PHONY: build-all build-api build-admin build-node build-queue build-scheduler build-rpc-core \
|
||||||
gen-api gen-model docker-up docker-down docker-env-up docker-env-down lint clean \
|
gen-api gen-model docker-up docker-down docker-env-up docker-env-down lint clean \
|
||||||
run-api run-admin run-node run-queue run-scheduler run-rpc-core
|
run-api run-admin run-node run-queue run-scheduler run-rpc-core migrate migrate-up migrate-down migrate-create migrate-version migrate-force migrate-goto
|
||||||
|
|
||||||
# Environment: dev | test | prod (default: dev)
|
# Environment: dev | test | prod (default: dev)
|
||||||
ENV ?= dev
|
ENV ?= dev
|
||||||
@@ -81,3 +81,36 @@ lint:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin/
|
rm -rf bin/
|
||||||
|
|
||||||
|
# Database migrations (using go-migrate)
|
||||||
|
migrate:
|
||||||
|
@echo "Usage: make migrate <command> [args...]"
|
||||||
|
@echo "Commands: up, down [version], create <name>, version, force <version>, goto <version>"
|
||||||
|
|
||||||
|
migrate-up:
|
||||||
|
@echo "Running database migration UP..."
|
||||||
|
go run pkg/migrate/main.go up
|
||||||
|
|
||||||
|
migrate-down:
|
||||||
|
@echo "Running database migration DOWN..."
|
||||||
|
ifeq ($(ARGS),)
|
||||||
|
go run pkg/migrate/main.go down
|
||||||
|
else
|
||||||
|
go run pkg/migrate/main.go down $(ARGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
migrate-create:
|
||||||
|
@echo "Creating new migration files..."
|
||||||
|
go run pkg/migrate/main.go create $(NAME)
|
||||||
|
|
||||||
|
migrate-version:
|
||||||
|
@echo "Getting current database migration version..."
|
||||||
|
go run pkg/migrate/main.go version
|
||||||
|
|
||||||
|
migrate-force:
|
||||||
|
@echo "Forcing database migration version..."
|
||||||
|
go run pkg/migrate/main.go force $(VERSION)
|
||||||
|
|
||||||
|
migrate-goto:
|
||||||
|
@echo "Going to specific database migration version..."
|
||||||
|
go run pkg/migrate/main.go goto $(VERSION)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
common "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/common"
|
common "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/common"
|
||||||
console "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/console"
|
console "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/console"
|
||||||
order "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/order"
|
order "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/order"
|
||||||
server serverhandler "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"
|
serverhandler "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"
|
||||||
subscribe "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/subscribe"
|
subscribe "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/subscribe"
|
||||||
system "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/system"
|
system "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/system"
|
||||||
ticket "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/ticket"
|
ticket "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/ticket"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
|
|
||||||
server := rest.MustNewServer(c.RestConf)
|
server := rest.MustNewServer(c.RestConf)
|
||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ type (
|
|||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeviceLoginReq {
|
||||||
|
Identifier string `json:"identifier" validate:"required"`
|
||||||
|
UserAgent string `json:"user_agent" validate:"required"`
|
||||||
|
ShortCode string `json:"short_code,optional"`
|
||||||
|
CfToken string `json:"cf_token,optional"`
|
||||||
|
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
UserRegisterReq {
|
UserRegisterReq {
|
||||||
Identifier string `json:"identifier"`
|
Identifier string `json:"identifier"`
|
||||||
Email string `json:"email" validate:"required"`
|
Email string `json:"email" validate:"required"`
|
||||||
@@ -58,3 +66,14 @@ service ppanel-api {
|
|||||||
post /reset (ResetPasswordReq) returns (LoginResp)
|
post /reset (ResetPasswordReq) returns (LoginResp)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
prefix: /api/v1/auth
|
||||||
|
group: auth
|
||||||
|
middleware: DecryptMiddleware
|
||||||
|
)
|
||||||
|
service ppanel-api {
|
||||||
|
@doc "设备登录"
|
||||||
|
@handler DeviceLoginHandler
|
||||||
|
post /login/device (DeviceLoginReq) returns (LoginResp)
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ CacheRedis:
|
|||||||
|
|
||||||
AppSignature:
|
AppSignature:
|
||||||
AppSecrets:
|
AppSecrets:
|
||||||
android-client: "uB4G,XxL2{7b"
|
android-client: "uB4G,XxL2{7b}"
|
||||||
web-client: "uB4G,XxL2{7b"
|
web-client: "uB4G,XxL2{7b}"
|
||||||
ios-client: "uB4G,XxL2{7b"
|
ios-client: "uB4G,XxL2{7b}"
|
||||||
mac-client: "uB4G,XxL2{7b"
|
mac-client: "uB4G,XxL2{7b}"
|
||||||
ValidWindowSeconds: 300
|
ValidWindowSeconds: 300
|
||||||
SkipPrefixes:
|
SkipPrefixes:
|
||||||
- /api/v1/health
|
- /api/v1/health
|
||||||
@@ -49,6 +49,9 @@ Security:
|
|||||||
Enable: true
|
Enable: true
|
||||||
SecuritySecret: "uB4G,XxL2{7b"
|
SecuritySecret: "uB4G,XxL2{7b"
|
||||||
|
|
||||||
|
Device:
|
||||||
|
Enable: true
|
||||||
|
|
||||||
Asynq:
|
Asynq:
|
||||||
Addr: 127.0.0.1:6379
|
Addr: 127.0.0.1:6379
|
||||||
|
|
||||||
|
|||||||
@@ -57,3 +57,6 @@ Security:
|
|||||||
Asynq:
|
Asynq:
|
||||||
Addr: "${REDIS_HOST}"
|
Addr: "${REDIS_HOST}"
|
||||||
Pass: "${REDIS_PASS}"
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
CoreRpc:
|
||||||
|
Target: "${CORE_RPC_TARGET}"
|
||||||
|
|||||||
@@ -23,4 +23,7 @@ type Config struct {
|
|||||||
Enable bool
|
Enable bool
|
||||||
SecuritySecret string
|
SecuritySecret string
|
||||||
}
|
}
|
||||||
|
Device struct {
|
||||||
|
Enable bool
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Code scaffolded by goctl. Safe to edit.
|
||||||
|
// goctl 1.9.2
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设备登录
|
||||||
|
func DeviceLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.DeviceLoginReq
|
||||||
|
if err := result.Parse(r, &req); err != nil {
|
||||||
|
result.HttpResult(r, w, nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := auth.NewDeviceLoginLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.DeviceLogin(&req)
|
||||||
|
result.HttpResult(r, w, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,21 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
rest.WithPrefix("/api/v1/auth"),
|
rest.WithPrefix("/api/v1/auth"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.DecryptMiddleware},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 设备登录
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/login/device",
|
||||||
|
Handler: auth.DeviceLoginHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithPrefix("/api/v1/auth"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// Code scaffolded by goctl. Safe to edit.
|
||||||
|
// goctl 1.9.2
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/pkg/jwtx"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceLoginLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备登录
|
||||||
|
func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceLoginLogic {
|
||||||
|
return &DeviceLoginLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginReq) (resp *types.LoginResp, err error) {
|
||||||
|
// todo: 新建设备登录是否禁用逻辑
|
||||||
|
if !l.svcCtx.Config.Device.Enable {
|
||||||
|
return nil, xerr.NewErrCode(xerr.DeviceLoginDisabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 调用 Core RPC 设备登录
|
||||||
|
rpcResp, err := l.svcCtx.CoreRpc.DeviceLogin(l.ctx, &core.DeviceLoginReq{
|
||||||
|
Identifier: req.Identifier,
|
||||||
|
UserAgent: req.UserAgent,
|
||||||
|
Ip: req.IP,
|
||||||
|
ShortCode: req.ShortCode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查用户是否禁用
|
||||||
|
if rpcResp.IsDisabled {
|
||||||
|
return nil, xerr.NewErrCode(xerr.UserDisabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 生成 session ID
|
||||||
|
sessionId := uuid.New().String()
|
||||||
|
|
||||||
|
// 4. 生成 JWT Token
|
||||||
|
token, err := jwtx.GenerateToken(
|
||||||
|
l.svcCtx.Config.JwtAuth.AccessSecret,
|
||||||
|
rpcResp.UserId,
|
||||||
|
"device",
|
||||||
|
rpcResp.IsAdmin,
|
||||||
|
l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("GenerateToken error: %v", err)
|
||||||
|
return nil, xerr.NewErrCode(xerr.ServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 写入 Redis session
|
||||||
|
expireDuration := time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire) * time.Second
|
||||||
|
|
||||||
|
// session:{sessionId} → userId
|
||||||
|
sessionKey := fmt.Sprintf("session:%s", sessionId)
|
||||||
|
if err := l.svcCtx.Redis.SetexCtx(l.ctx, sessionKey, fmt.Sprintf("%d", rpcResp.UserId), int(expireDuration.Seconds())); err != nil {
|
||||||
|
l.Errorf("Redis set session error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// device:{identifier} → sessionId
|
||||||
|
deviceKey := fmt.Sprintf("device:%s", req.Identifier)
|
||||||
|
if err := l.svcCtx.Redis.SetexCtx(l.ctx, deviceKey, sessionId, int(expireDuration.Seconds())); err != nil {
|
||||||
|
l.Errorf("Redis set device session error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.LoginResp{
|
||||||
|
Token: token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||||
@@ -87,6 +88,10 @@ func (m *DecryptMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Body = io.NopCloser(bytes.NewBuffer(plain))
|
r.Body = io.NopCloser(bytes.NewBuffer(plain))
|
||||||
|
// 防止 httpx.Parse 内部 r.ParseForm() 消费已替换的 body。
|
||||||
|
// Go 标准库 ParseForm 首行检查 r.PostForm==nil 才读 body,提前置空即可跳过。
|
||||||
|
r.PostForm = url.Values{}
|
||||||
|
r.ContentLength = int64(len(plain))
|
||||||
}
|
}
|
||||||
|
|
||||||
next(rw, r)
|
next(rw, r)
|
||||||
|
|||||||
@@ -6,17 +6,19 @@ package svc
|
|||||||
import (
|
import (
|
||||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/middleware"
|
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/middleware"
|
||||||
coreClient "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreclient"
|
coreClient "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreClient"
|
||||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
"github.com/zeromicro/go-zero/zrpc"
|
"github.com/zeromicro/go-zero/zrpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
CoreRpc coreClient.Core
|
CoreRpc coreClient.Core
|
||||||
|
Redis *redis.Redis
|
||||||
SignatureMiddleware *middleware.SignatureMiddleware
|
SignatureMiddleware *middleware.SignatureMiddleware
|
||||||
DecryptMiddleware *middleware.DecryptMiddleware
|
DecryptMiddleware rest.Middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
@@ -26,7 +28,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
|
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
|
||||||
|
Redis: rds,
|
||||||
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
||||||
DecryptMiddleware: middleware.NewDecryptMiddleware(c),
|
DecryptMiddleware: middleware.NewDecryptMiddleware(c).Handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ type CreateTicketReq struct {
|
|||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeviceLoginReq struct {
|
||||||
|
Identifier string `json:"identifier" validate:"required"`
|
||||||
|
UserAgent string `json:"user_agent" validate:"required"`
|
||||||
|
ShortCode string `json:"short_code,optional"`
|
||||||
|
CfToken string `json:"cf_token,optional"`
|
||||||
|
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
type DocumentDetailReq struct {
|
type DocumentDetailReq struct {
|
||||||
Id int64 `path:"id"`
|
Id int64 `path:"id"`
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -23,14 +23,13 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
|
|
||||||
server := rest.MustNewServer(c.RestConf)
|
server := rest.MustNewServer(c.RestConf)
|
||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
|
|
||||||
ctx := svc.NewServiceContext(c)
|
ctx := svc.NewServiceContext(c)
|
||||||
server.Use(ctx.SignatureMiddleware.Handle)
|
server.Use(ctx.SignatureMiddleware.Handle)
|
||||||
server.Use(ctx.DecryptMiddleware.Handle)
|
|
||||||
handler.RegisterHandlers(server, ctx)
|
handler.RegisterHandlers(server, ctx)
|
||||||
|
|
||||||
// Registe global http error handler
|
// Registe global http error handler
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
|
|
||||||
server := rest.MustNewServer(c.RestConf)
|
server := rest.MustNewServer(c.RestConf)
|
||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
|
|
||||||
logx.MustSetup(c.Log)
|
logx.MustSetup(c.Log)
|
||||||
if c.Telemetry.Name != "" {
|
if c.Telemetry.Name != "" {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
ctx := svc.NewServiceContext(c)
|
ctx := svc.NewServiceContext(c)
|
||||||
|
|
||||||
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
||||||
|
|||||||
@@ -45,6 +45,23 @@ message GetNodeInfoResp {
|
|||||||
string status = 4;
|
string status = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Device 服务定义
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
message DeviceLoginReq {
|
||||||
|
string identifier = 1;
|
||||||
|
string user_agent = 2;
|
||||||
|
string ip = 3;
|
||||||
|
string short_code = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeviceLoginResp {
|
||||||
|
int64 user_id = 1;
|
||||||
|
bool is_admin = 2;
|
||||||
|
bool is_disabled = 3;
|
||||||
|
bool is_new_user = 4;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// 核心 RPC 服务接口
|
// 核心 RPC 服务接口
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -57,4 +74,7 @@ service Core {
|
|||||||
|
|
||||||
// 节点相关
|
// 节点相关
|
||||||
rpc GetNodeInfo(GetNodeInfoReq) returns(GetNodeInfoResp);
|
rpc GetNodeInfo(GetNodeInfoReq) returns(GetNodeInfoResp);
|
||||||
|
|
||||||
|
// 设备登录
|
||||||
|
rpc DeviceLogin(DeviceLoginReq) returns(DeviceLoginResp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -374,6 +374,145 @@ func (x *GetNodeInfoResp) GetStatus() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Device 服务定义
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
type DeviceLoginReq struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
|
||||||
|
UserAgent string `protobuf:"bytes,2,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
|
||||||
|
Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||||
|
ShortCode string `protobuf:"bytes,4,opt,name=short_code,json=shortCode,proto3" json:"short_code,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) Reset() {
|
||||||
|
*x = DeviceLoginReq{}
|
||||||
|
mi := &file_core_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeviceLoginReq) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_core_proto_msgTypes[6]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DeviceLoginReq.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeviceLoginReq) Descriptor() ([]byte, []int) {
|
||||||
|
return file_core_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) GetIdentifier() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Identifier
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) GetUserAgent() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserAgent
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) GetIp() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Ip
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginReq) GetShortCode() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ShortCode
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceLoginResp struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||||
|
IsAdmin bool `protobuf:"varint,2,opt,name=is_admin,json=isAdmin,proto3" json:"is_admin,omitempty"`
|
||||||
|
IsDisabled bool `protobuf:"varint,3,opt,name=is_disabled,json=isDisabled,proto3" json:"is_disabled,omitempty"`
|
||||||
|
IsNewUser bool `protobuf:"varint,4,opt,name=is_new_user,json=isNewUser,proto3" json:"is_new_user,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) Reset() {
|
||||||
|
*x = DeviceLoginResp{}
|
||||||
|
mi := &file_core_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeviceLoginResp) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_core_proto_msgTypes[7]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DeviceLoginResp.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeviceLoginResp) Descriptor() ([]byte, []int) {
|
||||||
|
return file_core_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) GetUserId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) GetIsAdmin() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsAdmin
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) GetIsDisabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsDisabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceLoginResp) GetIsNewUser() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsNewUser
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var File_core_proto protoreflect.FileDescriptor
|
var File_core_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_core_proto_rawDesc = "" +
|
const file_core_proto_rawDesc = "" +
|
||||||
@@ -403,11 +542,27 @@ const file_core_proto_rawDesc = "" +
|
|||||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
|
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
|
||||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" +
|
"\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" +
|
||||||
"\x06server\x18\x03 \x01(\tR\x06server\x12\x16\n" +
|
"\x06server\x18\x03 \x01(\tR\x06server\x12\x16\n" +
|
||||||
"\x06status\x18\x04 \x01(\tR\x06status2\xa8\x01\n" +
|
"\x06status\x18\x04 \x01(\tR\x06status\"~\n" +
|
||||||
|
"\x0eDeviceLoginReq\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"identifier\x18\x01 \x01(\tR\n" +
|
||||||
|
"identifier\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"user_agent\x18\x02 \x01(\tR\tuserAgent\x12\x0e\n" +
|
||||||
|
"\x02ip\x18\x03 \x01(\tR\x02ip\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"short_code\x18\x04 \x01(\tR\tshortCode\"\x86\x01\n" +
|
||||||
|
"\x0fDeviceLoginResp\x12\x17\n" +
|
||||||
|
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x19\n" +
|
||||||
|
"\bis_admin\x18\x02 \x01(\bR\aisAdmin\x12\x1f\n" +
|
||||||
|
"\vis_disabled\x18\x03 \x01(\bR\n" +
|
||||||
|
"isDisabled\x12\x1e\n" +
|
||||||
|
"\vis_new_user\x18\x04 \x01(\bR\tisNewUser2\xe4\x01\n" +
|
||||||
"\x04Core\x12(\n" +
|
"\x04Core\x12(\n" +
|
||||||
"\x04Ping\x12\v.core.Empty\x1a\x13.core.BasicResponse\x12:\n" +
|
"\x04Ping\x12\v.core.Empty\x1a\x13.core.BasicResponse\x12:\n" +
|
||||||
"\vGetUserInfo\x12\x14.core.GetUserInfoReq\x1a\x15.core.GetUserInfoResp\x12:\n" +
|
"\vGetUserInfo\x12\x14.core.GetUserInfoReq\x1a\x15.core.GetUserInfoResp\x12:\n" +
|
||||||
"\vGetNodeInfo\x12\x14.core.GetNodeInfoReq\x1a\x15.core.GetNodeInfoRespB\bZ\x06./coreb\x06proto3"
|
"\vGetNodeInfo\x12\x14.core.GetNodeInfoReq\x1a\x15.core.GetNodeInfoResp\x12:\n" +
|
||||||
|
"\vDeviceLogin\x12\x14.core.DeviceLoginReq\x1a\x15.core.DeviceLoginRespB\bZ\x06./coreb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_core_proto_rawDescOnce sync.Once
|
file_core_proto_rawDescOnce sync.Once
|
||||||
@@ -421,7 +576,7 @@ func file_core_proto_rawDescGZIP() []byte {
|
|||||||
return file_core_proto_rawDescData
|
return file_core_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||||
var file_core_proto_goTypes = []any{
|
var file_core_proto_goTypes = []any{
|
||||||
(*Empty)(nil), // 0: core.Empty
|
(*Empty)(nil), // 0: core.Empty
|
||||||
(*BasicResponse)(nil), // 1: core.BasicResponse
|
(*BasicResponse)(nil), // 1: core.BasicResponse
|
||||||
@@ -429,16 +584,20 @@ var file_core_proto_goTypes = []any{
|
|||||||
(*GetUserInfoResp)(nil), // 3: core.GetUserInfoResp
|
(*GetUserInfoResp)(nil), // 3: core.GetUserInfoResp
|
||||||
(*GetNodeInfoReq)(nil), // 4: core.GetNodeInfoReq
|
(*GetNodeInfoReq)(nil), // 4: core.GetNodeInfoReq
|
||||||
(*GetNodeInfoResp)(nil), // 5: core.GetNodeInfoResp
|
(*GetNodeInfoResp)(nil), // 5: core.GetNodeInfoResp
|
||||||
|
(*DeviceLoginReq)(nil), // 6: core.DeviceLoginReq
|
||||||
|
(*DeviceLoginResp)(nil), // 7: core.DeviceLoginResp
|
||||||
}
|
}
|
||||||
var file_core_proto_depIdxs = []int32{
|
var file_core_proto_depIdxs = []int32{
|
||||||
0, // 0: core.Core.Ping:input_type -> core.Empty
|
0, // 0: core.Core.Ping:input_type -> core.Empty
|
||||||
2, // 1: core.Core.GetUserInfo:input_type -> core.GetUserInfoReq
|
2, // 1: core.Core.GetUserInfo:input_type -> core.GetUserInfoReq
|
||||||
4, // 2: core.Core.GetNodeInfo:input_type -> core.GetNodeInfoReq
|
4, // 2: core.Core.GetNodeInfo:input_type -> core.GetNodeInfoReq
|
||||||
1, // 3: core.Core.Ping:output_type -> core.BasicResponse
|
6, // 3: core.Core.DeviceLogin:input_type -> core.DeviceLoginReq
|
||||||
3, // 4: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
|
1, // 4: core.Core.Ping:output_type -> core.BasicResponse
|
||||||
5, // 5: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
|
3, // 5: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
|
||||||
3, // [3:6] is the sub-list for method output_type
|
5, // 6: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
|
||||||
0, // [0:3] is the sub-list for method input_type
|
7, // 7: core.Core.DeviceLogin:output_type -> core.DeviceLoginResp
|
||||||
|
4, // [4:8] is the sub-list for method output_type
|
||||||
|
0, // [0:4] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
0, // [0:0] is the sub-list for field type_name
|
0, // [0:0] is the sub-list for field type_name
|
||||||
@@ -455,7 +614,7 @@ func file_core_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_proto_rawDesc), len(file_core_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_proto_rawDesc), len(file_core_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 6,
|
NumMessages: 8,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const (
|
|||||||
Core_Ping_FullMethodName = "/core.Core/Ping"
|
Core_Ping_FullMethodName = "/core.Core/Ping"
|
||||||
Core_GetUserInfo_FullMethodName = "/core.Core/GetUserInfo"
|
Core_GetUserInfo_FullMethodName = "/core.Core/GetUserInfo"
|
||||||
Core_GetNodeInfo_FullMethodName = "/core.Core/GetNodeInfo"
|
Core_GetNodeInfo_FullMethodName = "/core.Core/GetNodeInfo"
|
||||||
|
Core_DeviceLogin_FullMethodName = "/core.Core/DeviceLogin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoreClient is the client API for Core service.
|
// CoreClient is the client API for Core service.
|
||||||
@@ -38,6 +39,8 @@ type CoreClient interface {
|
|||||||
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
|
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
|
||||||
// 节点相关
|
// 节点相关
|
||||||
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
|
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
|
||||||
|
// 设备登录
|
||||||
|
DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type coreClient struct {
|
type coreClient struct {
|
||||||
@@ -78,6 +81,16 @@ func (c *coreClient) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts .
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *coreClient) DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(DeviceLoginResp)
|
||||||
|
err := c.cc.Invoke(ctx, Core_DeviceLogin_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CoreServer is the server API for Core service.
|
// CoreServer is the server API for Core service.
|
||||||
// All implementations must embed UnimplementedCoreServer
|
// All implementations must embed UnimplementedCoreServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
@@ -92,6 +105,8 @@ type CoreServer interface {
|
|||||||
GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error)
|
GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error)
|
||||||
// 节点相关
|
// 节点相关
|
||||||
GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error)
|
GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error)
|
||||||
|
// 设备登录
|
||||||
|
DeviceLogin(context.Context, *DeviceLoginReq) (*DeviceLoginResp, error)
|
||||||
mustEmbedUnimplementedCoreServer()
|
mustEmbedUnimplementedCoreServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +126,9 @@ func (UnimplementedCoreServer) GetUserInfo(context.Context, *GetUserInfoReq) (*G
|
|||||||
func (UnimplementedCoreServer) GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error) {
|
func (UnimplementedCoreServer) GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetNodeInfo not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetNodeInfo not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedCoreServer) DeviceLogin(context.Context, *DeviceLoginReq) (*DeviceLoginResp, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method DeviceLogin not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedCoreServer) mustEmbedUnimplementedCoreServer() {}
|
func (UnimplementedCoreServer) mustEmbedUnimplementedCoreServer() {}
|
||||||
func (UnimplementedCoreServer) testEmbeddedByValue() {}
|
func (UnimplementedCoreServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
@@ -186,6 +204,24 @@ func _Core_GetNodeInfo_Handler(srv interface{}, ctx context.Context, dec func(in
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Core_DeviceLogin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(DeviceLoginReq)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(CoreServer).DeviceLogin(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Core_DeviceLogin_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(CoreServer).DeviceLogin(ctx, req.(*DeviceLoginReq))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// Core_ServiceDesc is the grpc.ServiceDesc for Core service.
|
// Core_ServiceDesc is the grpc.ServiceDesc for Core service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -205,6 +241,10 @@ var Core_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "GetNodeInfo",
|
MethodName: "GetNodeInfo",
|
||||||
Handler: _Core_GetNodeInfo_Handler,
|
Handler: _Core_GetNodeInfo_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "DeviceLogin",
|
||||||
|
Handler: _Core_DeviceLogin_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "core.proto",
|
Metadata: "core.proto",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// goctl 1.9.2
|
// goctl 1.9.2
|
||||||
// Source: core.proto
|
// Source: core.proto
|
||||||
|
|
||||||
package coreclient
|
package coreClient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -14,12 +14,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
BasicResponse = core.BasicResponse
|
BasicResponse = core.BasicResponse
|
||||||
Empty = core.Empty
|
DeviceLoginReq = core.DeviceLoginReq
|
||||||
GetNodeInfoReq = core.GetNodeInfoReq
|
DeviceLoginResp = core.DeviceLoginResp
|
||||||
GetNodeInfoResp = core.GetNodeInfoResp
|
Empty = core.Empty
|
||||||
GetUserInfoReq = core.GetUserInfoReq
|
GetNodeInfoReq = core.GetNodeInfoReq
|
||||||
GetUserInfoResp = core.GetUserInfoResp
|
GetNodeInfoResp = core.GetNodeInfoResp
|
||||||
|
GetUserInfoReq = core.GetUserInfoReq
|
||||||
|
GetUserInfoResp = core.GetUserInfoResp
|
||||||
|
|
||||||
Core interface {
|
Core interface {
|
||||||
// Ping 检查服务健康状态
|
// Ping 检查服务健康状态
|
||||||
@@ -28,6 +30,8 @@ type (
|
|||||||
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
|
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
|
||||||
// 节点相关
|
// 节点相关
|
||||||
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
|
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
|
||||||
|
// 设备登录
|
||||||
|
DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCore struct {
|
defaultCore struct {
|
||||||
@@ -58,3 +62,9 @@ func (m *defaultCore) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts
|
|||||||
client := core.NewCoreClient(m.cli.Conn())
|
client := core.NewCoreClient(m.cli.Conn())
|
||||||
return client.GetNodeInfo(ctx, in, opts...)
|
return client.GetNodeInfo(ctx, in, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设备登录
|
||||||
|
func (m *defaultCore) DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error) {
|
||||||
|
client := core.NewCoreClient(m.cli.Conn())
|
||||||
|
return client.DeviceLogin(ctx, in, opts...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/repo"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/svc"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceLoginLogic struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
logx.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceLoginLogic {
|
||||||
|
return &DeviceLoginLogic{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceLogin 设备登录:查设备 → 不存在则注册 → 返回用户信息
|
||||||
|
func (l *DeviceLoginLogic) DeviceLogin(in *core.DeviceLoginReq) (*core.DeviceLoginResp, error) {
|
||||||
|
// 1. 查询设备
|
||||||
|
device, err := l.svcCtx.DeviceRepo.FindDeviceByIdentifier(l.ctx, in.Identifier)
|
||||||
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
l.Errorf("FindDeviceByIdentifier error: %v", err)
|
||||||
|
return nil, status.Error(codes.Code(xerr.DatabaseQueryError), "查询设备失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
var userID int64
|
||||||
|
var isNewUser bool
|
||||||
|
var isAdmin bool
|
||||||
|
|
||||||
|
if errors.Is(err, sql.ErrNoRows) || device == nil {
|
||||||
|
// 2. 设备不存在,创建用户+设备
|
||||||
|
userID, err = l.svcCtx.DeviceRepo.CreateUserWithDevice(l.ctx, repo.CreateUserWithDeviceParams{
|
||||||
|
Identifier: in.Identifier,
|
||||||
|
UserAgent: in.UserAgent,
|
||||||
|
IP: in.Ip,
|
||||||
|
ShortCode: in.ShortCode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("CreateUserWithDevice error: %v", err)
|
||||||
|
return nil, status.Error(codes.Code(xerr.DatabaseInsertError), "创建用户设备失败")
|
||||||
|
}
|
||||||
|
isNewUser = true
|
||||||
|
|
||||||
|
// 记录注册日志
|
||||||
|
_ = l.svcCtx.DeviceRepo.InsertLoginLog(l.ctx, repo.LoginLogParams{
|
||||||
|
UserID: userID,
|
||||||
|
Method: "device",
|
||||||
|
IP: in.Ip,
|
||||||
|
UserAgent: in.UserAgent,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 3. 设备存在,检查是否启用
|
||||||
|
if !device.Enabled {
|
||||||
|
return nil, status.Error(codes.Code(xerr.DeviceNotEnabled), "设备已禁用")
|
||||||
|
}
|
||||||
|
|
||||||
|
userID = device.UserID
|
||||||
|
|
||||||
|
// 查用户信息
|
||||||
|
user, err := l.svcCtx.DeviceRepo.FindUserByID(l.ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorf("FindUserByID error: %v", err)
|
||||||
|
return nil, status.Error(codes.Code(xerr.DatabaseQueryError), "查询用户失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.Enable {
|
||||||
|
return nil, status.Error(codes.Code(xerr.UserDisabled), "用户已禁用")
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdmin = user.IsAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 记录登录日志
|
||||||
|
_ = l.svcCtx.DeviceRepo.InsertLoginLog(l.ctx, repo.LoginLogParams{
|
||||||
|
UserID: userID,
|
||||||
|
Method: "device",
|
||||||
|
IP: in.Ip,
|
||||||
|
UserAgent: in.UserAgent,
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &core.DeviceLoginResp{
|
||||||
|
UserId: userID,
|
||||||
|
IsAdmin: isAdmin,
|
||||||
|
IsDisabled: false,
|
||||||
|
IsNewUser: isNewUser,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/pkg/cryptox"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeviceInfo represents a row from user_device table.
|
||||||
|
type DeviceInfo struct {
|
||||||
|
ID int64 `db:"id"`
|
||||||
|
UserID int64 `db:"user_id"`
|
||||||
|
Identifier string `db:"Identifier"`
|
||||||
|
Enabled bool `db:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserInfo represents minimal user fields needed for login.
|
||||||
|
type UserInfo struct {
|
||||||
|
ID int64 `db:"id"`
|
||||||
|
Enable bool `db:"enable"`
|
||||||
|
IsAdmin bool `db:"is_admin"`
|
||||||
|
IsDeleted bool `db:"is_del"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserWithDeviceParams holds parameters for creating a user and device in one transaction.
|
||||||
|
type CreateUserWithDeviceParams struct {
|
||||||
|
Identifier string
|
||||||
|
UserAgent string
|
||||||
|
IP string
|
||||||
|
ShortCode string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginLogParams holds parameters for inserting a login log.
|
||||||
|
type LoginLogParams struct {
|
||||||
|
UserID int64
|
||||||
|
Method string
|
||||||
|
IP string
|
||||||
|
UserAgent string
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceRepo defines device-related data access methods.
|
||||||
|
type DeviceRepo interface {
|
||||||
|
FindDeviceByIdentifier(ctx context.Context, identifier string) (*DeviceInfo, error)
|
||||||
|
FindUserByID(ctx context.Context, userID int64) (*UserInfo, error)
|
||||||
|
CreateUserWithDevice(ctx context.Context, params CreateUserWithDeviceParams) (int64, error)
|
||||||
|
InsertLoginLog(ctx context.Context, params LoginLogParams) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceRepo struct {
|
||||||
|
conn sqlx.SqlConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceRepo(conn sqlx.SqlConn) DeviceRepo {
|
||||||
|
return &deviceRepo{conn: conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceRepo) FindDeviceByIdentifier(ctx context.Context, identifier string) (*DeviceInfo, error) {
|
||||||
|
var d DeviceInfo
|
||||||
|
err := r.conn.QueryRowCtx(ctx, &d,
|
||||||
|
"SELECT `id`, `user_id`, `Identifier`, `enabled` FROM `user_device` WHERE `Identifier` = ? LIMIT 1",
|
||||||
|
identifier,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceRepo) FindUserByID(ctx context.Context, userID int64) (*UserInfo, error) {
|
||||||
|
var u UserInfo
|
||||||
|
err := r.conn.QueryRowCtx(ctx, &u,
|
||||||
|
"SELECT `id`, `enable`, `is_admin`, COALESCE(`is_del`, 0) AS `is_del` FROM `user` WHERE `id` = ? LIMIT 1",
|
||||||
|
userID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceRepo) CreateUserWithDevice(ctx context.Context, params CreateUserWithDeviceParams) (int64, error) {
|
||||||
|
// Generate a random password hash for device-only users
|
||||||
|
randomPwd, err := cryptox.GeneratePasswordHash("device-placeholder-" + params.Identifier)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("hash password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var userID int64
|
||||||
|
err = r.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||||
|
// 1. Create user
|
||||||
|
result, err := session.ExecCtx(ctx,
|
||||||
|
"INSERT INTO `user` (`password`, `algo`, `salt`, `enable`, `is_admin`, `created_at`, `updated_at`) VALUES (?, 'bcrypt', 'default', 1, 0, NOW(3), NOW(3))",
|
||||||
|
randomPwd,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert user: %w", err)
|
||||||
|
}
|
||||||
|
userID, err = result.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("last insert id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create auth method
|
||||||
|
_, err = session.ExecCtx(ctx,
|
||||||
|
"INSERT INTO `user_auth_methods` (`user_id`, `auth_type`, `auth_identifier`, `verified`, `created_at`, `updated_at`) VALUES (?, 'device', ?, 1, NOW(3), NOW(3))",
|
||||||
|
userID, params.Identifier,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert auth method: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create device
|
||||||
|
_, err = session.ExecCtx(ctx,
|
||||||
|
"INSERT INTO `user_device` (`user_id`, `ip`, `Identifier`, `short_code`, `user_agent`, `online`, `enabled`, `created_at`, `updated_at`) VALUES (?, ?, ?, ?, ?, 0, 1, NOW(3), NOW(3))",
|
||||||
|
userID, params.IP, params.Identifier, params.ShortCode, params.UserAgent,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return userID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceRepo) InsertLoginLog(ctx context.Context, params LoginLogParams) error {
|
||||||
|
content, _ := json.Marshal(map[string]interface{}{
|
||||||
|
"method": params.Method,
|
||||||
|
"login_ip": params.IP,
|
||||||
|
"user_agent": params.UserAgent,
|
||||||
|
"success": params.Success,
|
||||||
|
"timestamp": time.Now().UnixMilli(),
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := r.conn.ExecCtx(ctx,
|
||||||
|
"INSERT INTO `system_logs` (`type`, `date`, `object_id`, `content`, `created_at`) VALUES (?, ?, ?, ?, NOW(3))",
|
||||||
|
6, // type=6 is Login log
|
||||||
|
time.Now().Format("2006-01-02"),
|
||||||
|
params.UserID,
|
||||||
|
string(content),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("insert login log: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNotFound is re-exported for convenience.
|
||||||
|
var ErrNotFound = sql.ErrNoRows
|
||||||
@@ -40,3 +40,9 @@ func (s *CoreServer) GetNodeInfo(ctx context.Context, in *core.GetNodeInfoReq) (
|
|||||||
l := logic.NewGetNodeInfoLogic(ctx, s.svcCtx)
|
l := logic.NewGetNodeInfoLogic(ctx, s.svcCtx)
|
||||||
return l.GetNodeInfo(in)
|
return l.GetNodeInfo(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设备登录
|
||||||
|
func (s *CoreServer) DeviceLogin(ctx context.Context, in *core.DeviceLoginReq) (*core.DeviceLoginResp, error) {
|
||||||
|
l := logic.NewDeviceLoginLogic(ctx, s.svcCtx)
|
||||||
|
return l.DeviceLogin(in)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
package svc
|
package svc
|
||||||
|
|
||||||
import "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/config"
|
import (
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/config"
|
||||||
|
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/repo"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
|
DB sqlx.SqlConn
|
||||||
|
Redis *redis.Redis
|
||||||
|
DeviceRepo repo.DeviceRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
db := sqlx.NewMysql(c.MySQL.DataSource)
|
||||||
|
rds := redis.MustNewRedis(c.CacheRedis)
|
||||||
|
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
|
DB: db,
|
||||||
|
Redis: rds,
|
||||||
|
DeviceRepo: repo.NewDeviceRepo(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var c config.Config
|
var c config.Config
|
||||||
conf.MustLoad(*configFile, &c)
|
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||||
|
|
||||||
logx.MustSetup(c.Log)
|
logx.MustSetup(c.Log)
|
||||||
if c.Telemetry.Name != "" {
|
if c.Telemetry.Name != "" {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
PPANEL_REPO=registry.kxsw.us/vpn-server
|
||||||
|
PPANEL_TAG=latest
|
||||||
|
|
||||||
|
# 如果密码包含 $,请写成 $$,避免被 docker compose 当成变量展开
|
||||||
|
MYSQL_DSN=root:replace-with-db-password@tcp(mysql:3306)/ppanel?charset=utf8mb4&parseTime=true
|
||||||
|
REDIS_HOST=redis:6379
|
||||||
|
REDIS_PASS= kP9#vR2$mX7
|
||||||
|
|
||||||
|
JWT_SECRET=replace-with-strong-secret
|
||||||
|
JWT_ADMIN_SECRET=replace-with-strong-secret
|
||||||
|
APP_SECRET=replace-with-strong-secret
|
||||||
|
SECURITY_SECRET=replace-with-strong-secret
|
||||||
|
NODE_SECRET=replace-with-strong-secret
|
||||||
|
|
||||||
|
CORE_RPC_TARGET=ppanel-rpc-core:8083
|
||||||
+10
-4
@@ -1,11 +1,17 @@
|
|||||||
FROM golang:1.23-alpine AS builder
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY apps/admin ./apps/admin
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -o /ppanel-admin ./apps/admin/admin.go
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-admin ./apps/admin/ppaneladmin.go
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
+11
-4
@@ -1,11 +1,18 @@
|
|||||||
FROM golang:1.23-alpine AS builder
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY apps/api ./apps/api
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -o /ppanel-api ./apps/api/api.go
|
COPY apps/rpc ./apps/rpc
|
||||||
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-api ./apps/api/ppanel.go
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
+10
-4
@@ -1,11 +1,17 @@
|
|||||||
FROM golang:1.23-alpine AS builder
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY apps/node ./apps/node
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -o /ppanel-node ./apps/node/node.go
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-node ./apps/node/ppanelnode.go
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
+10
-4
@@ -1,11 +1,17 @@
|
|||||||
FROM golang:1.23-alpine AS builder
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY apps/queue ./apps/queue
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -o /ppanel-queue ./apps/queue/queue.go
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-queue ./apps/queue/queue.go
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
|
COPY apps/rpc ./apps/rpc
|
||||||
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-rpc-core ./apps/rpc/core/core.go
|
||||||
|
|
||||||
|
FROM alpine:3.19
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /ppanel-rpc-core .
|
||||||
|
COPY apps/rpc/core/etc /app/etc
|
||||||
|
|
||||||
|
EXPOSE 8083
|
||||||
|
CMD ["./ppanel-rpc-core", "-f", "etc/core.yaml"]
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
FROM golang:1.23-alpine AS builder
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY apps/scheduler ./apps/scheduler
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -o /ppanel-scheduler ./apps/scheduler/scheduler.go
|
COPY pkg ./pkg
|
||||||
|
RUN --mount=type=cache,target=/root/go/pkg/mod,id=gomodcache \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build,id=gobuildcache \
|
||||||
|
CGO_ENABLED=0 go build -trimpath -o /ppanel-scheduler ./apps/scheduler/scheduler.go
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ services:
|
|||||||
MYSQL_DATABASE: ppanel
|
MYSQL_DATABASE: ppanel
|
||||||
volumes:
|
volumes:
|
||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
- ../sql:/docker-entrypoint-initdb.d
|
# - ../sql:/docker-entrypoint-initdb.d
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASS:?REDIS_PASS is required}"]
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "6379:6379"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -2,12 +2,29 @@
|
|||||||
# 镜像由 CI 预先构建并推送到镜像仓库
|
# 镜像由 CI 预先构建并推送到镜像仓库
|
||||||
# 本地基础设施 (mysql/redis) 需提前独立部署
|
# 本地基础设施 (mysql/redis) 需提前独立部署
|
||||||
|
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
ppanel-rpc-core:
|
||||||
|
image: ${PPANEL_REPO}/ppanel-rpc-core:${PPANEL_TAG:-latest}
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
ports:
|
||||||
|
- "8083:8083"
|
||||||
|
volumes:
|
||||||
|
- ./etc/core:/app/etc
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
ppanel-api:
|
ppanel-api:
|
||||||
image: ${PPANEL_REPO}/ppanel-api:${PPANEL_TAG:-latest}
|
image: ${PPANEL_REPO}/ppanel-api:${PPANEL_TAG:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- ppanel-rpc-core
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -21,6 +38,8 @@ services:
|
|||||||
ppanel-admin:
|
ppanel-admin:
|
||||||
image: ${PPANEL_REPO}/ppanel-admin:${PPANEL_TAG:-latest}
|
image: ${PPANEL_REPO}/ppanel-admin:${PPANEL_TAG:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
ports:
|
ports:
|
||||||
- "8081:8081"
|
- "8081:8081"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -34,6 +53,8 @@ services:
|
|||||||
ppanel-node:
|
ppanel-node:
|
||||||
image: ${PPANEL_REPO}/ppanel-node:${PPANEL_TAG:-latest}
|
image: ${PPANEL_REPO}/ppanel-node:${PPANEL_TAG:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
ports:
|
ports:
|
||||||
- "8082:8082"
|
- "8082:8082"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -47,6 +68,8 @@ services:
|
|||||||
ppanel-queue:
|
ppanel-queue:
|
||||||
image: ${PPANEL_REPO}/ppanel-queue:${PPANEL_TAG:-latest}
|
image: ${PPANEL_REPO}/ppanel-queue:${PPANEL_TAG:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./etc/queue:/app/etc
|
- ./etc/queue:/app/etc
|
||||||
logging:
|
logging:
|
||||||
@@ -58,6 +81,8 @@ services:
|
|||||||
ppanel-scheduler:
|
ppanel-scheduler:
|
||||||
image: ${PPANEL_REPO}/ppanel-scheduler:${PPANEL_TAG:-latest}
|
image: ${PPANEL_REPO}/ppanel-scheduler:${PPANEL_TAG:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./etc/scheduler:/app/etc
|
- ./etc/scheduler:/app/etc
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ version: '3.8'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
ppanel-api:
|
ppanel-api:
|
||||||
build:
|
image: "${REPO}/api:latest"
|
||||||
context: ..
|
|
||||||
dockerfile: deploy/Dockerfile.api
|
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
- "6160:6160"
|
- "6160:6160"
|
||||||
@@ -16,9 +14,7 @@ services:
|
|||||||
- ../apps/api/etc:/app/etc
|
- ../apps/api/etc:/app/etc
|
||||||
|
|
||||||
ppanel-admin:
|
ppanel-admin:
|
||||||
build:
|
image: "${REPO}/admin:latest"
|
||||||
context: ..
|
|
||||||
dockerfile: deploy/Dockerfile.admin
|
|
||||||
ports:
|
ports:
|
||||||
- "8081:8081"
|
- "8081:8081"
|
||||||
- "6161:6161"
|
- "6161:6161"
|
||||||
@@ -30,9 +26,7 @@ services:
|
|||||||
- ../apps/admin/etc:/app/etc
|
- ../apps/admin/etc:/app/etc
|
||||||
|
|
||||||
ppanel-node:
|
ppanel-node:
|
||||||
build:
|
image: "${REPO}/node:latest"
|
||||||
context: ..
|
|
||||||
dockerfile: deploy/Dockerfile.node
|
|
||||||
ports:
|
ports:
|
||||||
- "8082:8082"
|
- "8082:8082"
|
||||||
- "6162:6162"
|
- "6162:6162"
|
||||||
@@ -46,9 +40,7 @@ services:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
|
|
||||||
ppanel-queue:
|
ppanel-queue:
|
||||||
build:
|
image: "${REPO}/queue:latest"
|
||||||
context: ..
|
|
||||||
dockerfile: deploy/Dockerfile.queue
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
- redis
|
- redis
|
||||||
@@ -57,9 +49,7 @@ services:
|
|||||||
- ../apps/queue/etc:/app/etc
|
- ../apps/queue/etc:/app/etc
|
||||||
|
|
||||||
ppanel-scheduler:
|
ppanel-scheduler:
|
||||||
build:
|
image: "${REPO}/scheduler:latest"
|
||||||
context: ..
|
|
||||||
dockerfile: deploy/Dockerfile.scheduler
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
- redis
|
- redis
|
||||||
@@ -73,7 +63,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: password
|
MYSQL_ROOT_PASSWORD: pavR2$mX7*tQ
|
||||||
MYSQL_DATABASE: ppanel
|
MYSQL_DATABASE: ppanel
|
||||||
volumes:
|
volumes:
|
||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
Name: zero-ppanel-admin
|
||||||
|
Host: 0.0.0.0
|
||||||
|
Port: 8081
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/admin
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: zero-ppanel-admin
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
DevServer:
|
||||||
|
Enabled: true
|
||||||
|
Port: 6161
|
||||||
|
EnableMetrics: true
|
||||||
|
EnablePprof: false
|
||||||
|
|
||||||
|
JwtAuth:
|
||||||
|
AccessSecret: "${JWT_ADMIN_SECRET}"
|
||||||
|
AccessExpire: 28800
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
CacheRedis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
AppSignature:
|
||||||
|
AppSecrets:
|
||||||
|
android-client: "${APP_SECRET}"
|
||||||
|
web-client: "${APP_SECRET}"
|
||||||
|
ios-client: "${APP_SECRET}"
|
||||||
|
mac-client: "${APP_SECRET}"
|
||||||
|
ValidWindowSeconds: 300
|
||||||
|
SkipPrefixes:
|
||||||
|
- /api/v1/admin/health
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
Name: zero-ppanel-api
|
||||||
|
Host: 0.0.0.0
|
||||||
|
Port: 8080
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/api
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: zero-ppanel-api
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
DevServer:
|
||||||
|
Enabled: true
|
||||||
|
Port: 6160
|
||||||
|
EnableMetrics: true
|
||||||
|
EnablePprof: false
|
||||||
|
|
||||||
|
JwtAuth:
|
||||||
|
AccessSecret: "${JWT_SECRET}"
|
||||||
|
AccessExpire: 86400
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
CacheRedis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
AppSignature:
|
||||||
|
AppSecrets:
|
||||||
|
android-client: "${APP_SECRET}"
|
||||||
|
web-client: "${APP_SECRET}"
|
||||||
|
ios-client: "${APP_SECRET}"
|
||||||
|
mac-client: "${APP_SECRET}"
|
||||||
|
ValidWindowSeconds: 300
|
||||||
|
SkipPrefixes:
|
||||||
|
- /api/v1/health
|
||||||
|
|
||||||
|
Security:
|
||||||
|
Enable: true
|
||||||
|
SecuritySecret: "${SECURITY_SECRET}"
|
||||||
|
|
||||||
|
Asynq:
|
||||||
|
Addr: "${REDIS_HOST}"
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
CoreRpc:
|
||||||
|
Target: "${CORE_RPC_TARGET}"
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Name: core.rpc
|
||||||
|
ListenOn: 0.0.0.0:8083
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/rpc-core
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: core.rpc
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
DevServer:
|
||||||
|
Enabled: true
|
||||||
|
Port: 6163
|
||||||
|
EnableMetrics: true
|
||||||
|
EnablePprof: false
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
CacheRedis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
Name: zero-ppanel-node
|
||||||
|
Host: 0.0.0.0
|
||||||
|
Port: 8082
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/node
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: zero-ppanel-node
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
DevServer:
|
||||||
|
Enabled: true
|
||||||
|
Port: 6162
|
||||||
|
EnableMetrics: true
|
||||||
|
EnablePprof: false
|
||||||
|
|
||||||
|
NodeSecret: "${NODE_SECRET}"
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
CacheRedis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
AppSignature:
|
||||||
|
AppSecrets:
|
||||||
|
android-client: "${APP_SECRET}"
|
||||||
|
web-client: "${APP_SECRET}"
|
||||||
|
ios-client: "${APP_SECRET}"
|
||||||
|
mac-client: "${APP_SECRET}"
|
||||||
|
ValidWindowSeconds: 300
|
||||||
|
SkipPrefixes:
|
||||||
|
- /api/v1/node/health
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
Name: zero-ppanel-queue
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/queue
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: zero-ppanel-queue
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
Asynq:
|
||||||
|
Addr: "${REDIS_HOST}"
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
Name: zero-ppanel-scheduler
|
||||||
|
Mode: pro
|
||||||
|
|
||||||
|
Log:
|
||||||
|
Mode: file
|
||||||
|
Encoding: json
|
||||||
|
Level: info
|
||||||
|
Path: /var/log/zero-ppanel/scheduler
|
||||||
|
KeepDays: 15
|
||||||
|
Rotation: daily
|
||||||
|
|
||||||
|
Telemetry:
|
||||||
|
Name: zero-ppanel-scheduler
|
||||||
|
Endpoint: http://jaeger:4318/v1/traces
|
||||||
|
Sampler: 0.1
|
||||||
|
Batcher: otlphttp
|
||||||
|
|
||||||
|
MySQL:
|
||||||
|
DataSource: "${MYSQL_DSN}"
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
Host: "${REDIS_HOST}"
|
||||||
|
Type: node
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
|
|
||||||
|
Asynq:
|
||||||
|
Addr: "${REDIS_HOST}"
|
||||||
|
Pass: "${REDIS_PASS}"
|
||||||
@@ -3,36 +3,39 @@ module github.com/zero-ppanel/zero-ppanel
|
|||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||||
github.com/hibiken/asynq v0.25.1
|
github.com/hibiken/asynq v0.25.1
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/zeromicro/go-zero v1.7.6
|
github.com/zeromicro/go-zero v1.7.6
|
||||||
golang.org/x/crypto v0.48.0
|
golang.org/x/crypto v0.48.0
|
||||||
google.golang.org/grpc v1.65.0
|
google.golang.org/grpc v1.74.2
|
||||||
google.golang.org/protobuf v1.36.1
|
google.golang.org/protobuf v1.36.7
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/coreos/go-semver v0.3.1 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
@@ -58,29 +61,30 @@ require (
|
|||||||
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
|
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
|
||||||
go.etcd.io/etcd/client/v3 v3.5.15 // indirect
|
go.etcd.io/etcd/client/v3 v3.5.15 // indirect
|
||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
|
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
golang.org/x/net v0.49.0 // indirect
|
golang.org/x/net v0.49.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/term v0.40.0 // indirect
|
golang.org/x/term v0.40.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
golang.org/x/time v0.8.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0=
|
||||||
@@ -14,26 +20,43 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
|
|||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4=
|
||||||
|
github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU=
|
||||||
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
|
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
|
||||||
|
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||||
@@ -43,15 +66,19 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En
|
|||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
@@ -59,8 +86,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
|
|||||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@@ -74,6 +101,8 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
|
|||||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||||
github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw=
|
github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw=
|
||||||
github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg=
|
github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
@@ -91,6 +120,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@@ -98,25 +129,37 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
|
||||||
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
@@ -131,8 +174,8 @@ github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa
|
|||||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
@@ -165,12 +208,16 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5
|
|||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU=
|
||||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||||
|
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||||
|
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
||||||
@@ -179,12 +226,14 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDO
|
|||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
|
go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
|
go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM=
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
@@ -212,8 +261,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -235,8 +284,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
@@ -248,14 +297,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
+4
-3
@@ -13,10 +13,11 @@ type Claims struct {
|
|||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateToken(secret string, userId int64, isAdmin bool, expire int64) (string, error) {
|
func GenerateToken(secret string, userId int64, loginType string, isAdmin bool, expire int64) (string, error) {
|
||||||
claims := Claims{
|
claims := Claims{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
IsAdmin: isAdmin,
|
LoginType: loginType,
|
||||||
|
IsAdmin: isAdmin,
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)),
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/database/mysql"
|
||||||
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 加载 .env 文件
|
||||||
|
err := godotenv.Load(".env")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("未能加载 .env 文件,可能使用环境变量。")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
log.Fatalf("使用方法: go run main.go <command> [args...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
command := os.Args[1]
|
||||||
|
if command == "create" {
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
log.Fatalf("使用方法: go run main.go create <migration_name>")
|
||||||
|
}
|
||||||
|
createMigration(os.Args[2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseUrl := os.Getenv("DATABASE_DSN")
|
||||||
|
if databaseUrl == "" {
|
||||||
|
log.Fatalf("DATABASE_DSN 环境变量未设置")
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := migrate.New(
|
||||||
|
"file://sql", // 迁移文件路径
|
||||||
|
databaseUrl,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("初始化迁移工具失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if sourceErr, databaseErr := m.Close(); sourceErr != nil || databaseErr != nil {
|
||||||
|
log.Printf("关闭迁移工具时出错: sourceErr=%v, databaseErr=%v", sourceErr, databaseErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "up":
|
||||||
|
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||||
|
log.Fatalf("执行迁移 Up 失败: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("迁移 Up 成功")
|
||||||
|
case "down":
|
||||||
|
if len(os.Args) < 3 { // 不带版本号时回滚最新一个版本
|
||||||
|
if err := m.Down(); err != nil && err != migrate.ErrNoChange {
|
||||||
|
log.Fatalf("执行迁移 Down 失败: %v", err)
|
||||||
|
}
|
||||||
|
log.Println("迁移 Down 成功 (回滚一个版本)")
|
||||||
|
} else { // 带版本号时回滚到指定版本
|
||||||
|
version, err := strconv.Atoi(os.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("版本号错误: %v", err)
|
||||||
|
}
|
||||||
|
if err := m.Migrate(uint(version)); err != nil && err != migrate.ErrNoChange {
|
||||||
|
log.Fatalf("执行迁移 Migrate 到版本 %d 失败: %v", version, err)
|
||||||
|
}
|
||||||
|
log.Printf("迁移 Down 成功 (回滚到版本 %d)", version)
|
||||||
|
}
|
||||||
|
case "goto":
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
log.Fatalf("使用方法: go run main.go goto <version>")
|
||||||
|
}
|
||||||
|
version, err := strconv.Atoi(os.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("版本号错误: %v", err)
|
||||||
|
}
|
||||||
|
if err := m.Migrate(uint(version)); err != nil && err != migrate.ErrNoChange {
|
||||||
|
log.Fatalf("执行迁移 Goto 版本 %d 失败: %v", version, err)
|
||||||
|
}
|
||||||
|
log.Printf("迁移 Goto 成功 (到版本 %d)", version)
|
||||||
|
case "force":
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
log.Fatalf("使用方法: go run main.go force <version>")
|
||||||
|
}
|
||||||
|
version, err := strconv.Atoi(os.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("版本号错误: %v", err)
|
||||||
|
}
|
||||||
|
if err := m.Force(int(version)); err != nil {
|
||||||
|
log.Fatalf("强制设置版本 %d 失败: %v", version, err)
|
||||||
|
}
|
||||||
|
log.Printf("强制设置版本 %d 成功", version)
|
||||||
|
case "version":
|
||||||
|
version, dirty, err := m.Version()
|
||||||
|
if err != nil && err != migrate.ErrNoChange {
|
||||||
|
log.Fatalf("获取版本失败: %v", err)
|
||||||
|
}
|
||||||
|
if dirty {
|
||||||
|
log.Printf("当前数据库版本: %d (dirty)", version)
|
||||||
|
} else {
|
||||||
|
log.Printf("当前数据库版本: %d", version)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatalf("未知命令: %s. 支持的命令: up, down, goto, force, version, create", command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMigration(name string) {
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
upFilename := fmt.Sprintf("sql/%d_%s.up.sql", timestamp, name)
|
||||||
|
downFilename := fmt.Sprintf("sql/%d_%s.down.sql", timestamp, name)
|
||||||
|
|
||||||
|
if err := os.WriteFile(upFilename, []byte(""), 0644); err != nil {
|
||||||
|
log.Fatalf("创建 %s 失败: %v", upFilename, err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(downFilename, []byte(""), 0644); err != nil {
|
||||||
|
log.Fatalf("创建 %s 失败: %v", downFilename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("成功创建迁移文件: %s 和 %s", upFilename, downFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数,用于检查数据库连接是否正常
|
||||||
|
func checkDBConnection(dsn string) error {
|
||||||
|
db, err := sql.Open("mysql", dsn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无法连接数据库: %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
db.SetMaxOpenConns(1)
|
||||||
|
db.SetMaxIdleConns(1)
|
||||||
|
db.SetConnMaxLifetime(5 * time.Second)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err = db.PingContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无法 Ping 数据库: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
@@ -24,4 +24,6 @@ const (
|
|||||||
SignatureInvalid = 7003
|
SignatureInvalid = 7003
|
||||||
SignatureReplay = 7004
|
SignatureReplay = 7004
|
||||||
DecryptFailed = 7005
|
DecryptFailed = 7005
|
||||||
|
DeviceLoginDisabled = 8001
|
||||||
|
DeviceNotEnabled = 8002
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ var codeText = map[int]string{
|
|||||||
SignatureInvalid: "签名错误",
|
SignatureInvalid: "签名错误",
|
||||||
SignatureReplay: "重放攻击",
|
SignatureReplay: "重放攻击",
|
||||||
DecryptFailed: "解密失败",
|
DecryptFailed: "解密失败",
|
||||||
|
DeviceLoginDisabled: "设备登录未启用",
|
||||||
|
DeviceNotEnabled: "设备已禁用",
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsCodeErr(errcode uint32) bool {
|
func IsCodeErr(errcode uint32) bool {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `ads`;
|
||||||
|
DROP TABLE IF EXISTS `announcement`;
|
||||||
|
DROP TABLE IF EXISTS `auth_method`;
|
||||||
|
DROP TABLE IF EXISTS `coupon`;
|
||||||
|
DROP TABLE IF EXISTS `document`;
|
||||||
|
DROP TABLE IF EXISTS `nodes`;
|
||||||
|
DROP TABLE IF EXISTS `order`;
|
||||||
|
DROP TABLE IF EXISTS `payment`;
|
||||||
|
DROP TABLE IF EXISTS `redemption_code`;
|
||||||
|
DROP TABLE IF EXISTS `redemption_record`;
|
||||||
|
DROP TABLE IF EXISTS `server`;
|
||||||
|
DROP TABLE IF EXISTS `servers`;
|
||||||
|
DROP TABLE IF EXISTS `subscribe`;
|
||||||
|
DROP TABLE IF EXISTS `subscribe_application`;
|
||||||
|
DROP TABLE IF EXISTS `system`;
|
||||||
|
DROP TABLE IF EXISTS `system_logs`;
|
||||||
|
DROP TABLE IF EXISTS `task`;
|
||||||
|
DROP TABLE IF EXISTS `ticket`;
|
||||||
|
DROP TABLE IF EXISTS `ticket_follow`;
|
||||||
|
DROP TABLE IF EXISTS `traffic_log`;
|
||||||
|
DROP TABLE IF EXISTS `user`;
|
||||||
|
DROP TABLE IF EXISTS `user_auth_methods`;
|
||||||
|
DROP TABLE IF EXISTS `user_device`;
|
||||||
|
DROP TABLE IF EXISTS `user_device_online_record`;
|
||||||
|
DROP TABLE IF EXISTS `user_subscribe`;
|
||||||
|
DROP TABLE IF EXISTS `withdrawals`;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
@@ -0,0 +1,519 @@
|
|||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `ads` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Ads title',
|
||||||
|
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Ads type',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Ads content',
|
||||||
|
`target_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Ads target url',
|
||||||
|
`start_time` datetime DEFAULT NULL COMMENT 'Ads start time',
|
||||||
|
`end_time` datetime DEFAULT NULL COMMENT 'Ads end time',
|
||||||
|
`status` tinyint(1) DEFAULT '0' COMMENT 'Ads status,0 disable,1 enable',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
`description` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Description',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `announcement` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Content',
|
||||||
|
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Show',
|
||||||
|
`pinned` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Pinned',
|
||||||
|
`popup` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Popup',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `auth_method` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'method',
|
||||||
|
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'OAuth Configuration',
|
||||||
|
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Enabled',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_auth_method` (`method`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `coupon` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Coupon Name',
|
||||||
|
`code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Coupon Code',
|
||||||
|
`count` bigint NOT NULL DEFAULT '0' COMMENT 'Count Limit',
|
||||||
|
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Coupon Type: 1: Percentage 2: Fixed Amount',
|
||||||
|
`discount` bigint NOT NULL DEFAULT '0' COMMENT 'Coupon Discount',
|
||||||
|
`start_time` bigint NOT NULL DEFAULT '0' COMMENT 'Start Time',
|
||||||
|
`expire_time` bigint NOT NULL DEFAULT '0' COMMENT 'Expire Time',
|
||||||
|
`user_limit` bigint NOT NULL DEFAULT '0' COMMENT 'User Limit',
|
||||||
|
`subscribe` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subscribe Limit',
|
||||||
|
`used_count` bigint NOT NULL DEFAULT '0' COMMENT 'Used Count',
|
||||||
|
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enable',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_coupon_code` (`code`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `document` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Document Title',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Document Content',
|
||||||
|
`tags` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Document Tags',
|
||||||
|
`show` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Show',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `nodes` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Name',
|
||||||
|
`tags` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Tags',
|
||||||
|
`port` smallint unsigned NOT NULL DEFAULT '0' COMMENT 'Connect Port',
|
||||||
|
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Connect Address',
|
||||||
|
`server_id` bigint NOT NULL DEFAULT '0' COMMENT 'Server ID',
|
||||||
|
`protocol` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Protocol',
|
||||||
|
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enabled',
|
||||||
|
`sort` int unsigned NOT NULL DEFAULT '0' COMMENT 'Sort',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `order` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`parent_id` bigint DEFAULT NULL COMMENT 'Parent Order Id',
|
||||||
|
`user_id` bigint NOT NULL DEFAULT '0' COMMENT 'User Id',
|
||||||
|
`order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Order No',
|
||||||
|
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Order Type: 1: Subscribe, 2: Renewal, 3: ResetTraffic, 4: Recharge',
|
||||||
|
`quantity` bigint NOT NULL DEFAULT '1' COMMENT 'Quantity',
|
||||||
|
`price` bigint NOT NULL DEFAULT '0' COMMENT 'Original price',
|
||||||
|
`amount` bigint NOT NULL DEFAULT '0' COMMENT 'Order Amount',
|
||||||
|
`gift_amount` bigint NOT NULL DEFAULT '0' COMMENT 'User Gift Amount',
|
||||||
|
`discount` bigint NOT NULL DEFAULT '0' COMMENT 'Discount Amount',
|
||||||
|
`coupon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Coupon',
|
||||||
|
`coupon_discount` bigint NOT NULL DEFAULT '0' COMMENT 'Coupon Discount Amount',
|
||||||
|
`commission` bigint NOT NULL DEFAULT '0' COMMENT 'Order Commission',
|
||||||
|
`payment_id` bigint NOT NULL DEFAULT '-1' COMMENT 'Payment Id',
|
||||||
|
`method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Method',
|
||||||
|
`fee_amount` bigint NOT NULL DEFAULT '0' COMMENT 'Fee Amount',
|
||||||
|
`trade_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Trade No',
|
||||||
|
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Order Status: 1: Pending, 2: Paid, 3:Close, 4: Failed, 5:Finished',
|
||||||
|
`subscribe_id` bigint NOT NULL DEFAULT '0' COMMENT 'Subscribe Id',
|
||||||
|
`subscribe_token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Renewal Subscribe Token',
|
||||||
|
`is_new` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is New Order',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_order_order_no` (`order_no`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `payment` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Payment Name',
|
||||||
|
`platform` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Payment Platform',
|
||||||
|
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Payment Description',
|
||||||
|
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Payment Token',
|
||||||
|
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Payment Icon',
|
||||||
|
`domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Notification Domain',
|
||||||
|
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Payment Configuration',
|
||||||
|
`fee_mode` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Fee Mode: 0: No Fee 1: Percentage 2: Fixed Amount 3: Percentage + Fixed Amount',
|
||||||
|
`fee_percent` bigint DEFAULT '0' COMMENT 'Fee Percentage',
|
||||||
|
`fee_amount` bigint DEFAULT '0' COMMENT 'Fixed Fee Amount',
|
||||||
|
`enable` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Enabled',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `redemption_code` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||||
|
`code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Redemption Code',
|
||||||
|
`total_count` bigint NOT NULL DEFAULT '0' COMMENT 'Total Redemption Count',
|
||||||
|
`used_count` bigint NOT NULL DEFAULT '0' COMMENT 'Used Redemption Count',
|
||||||
|
`subscribe_plan` bigint NOT NULL DEFAULT '0' COMMENT 'Subscribe Plan',
|
||||||
|
`unit_time` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'month' COMMENT 'Unit Time: day, month, quarter, half_year, year',
|
||||||
|
`quantity` bigint NOT NULL DEFAULT '1' COMMENT 'Quantity',
|
||||||
|
`status` tinyint NOT NULL DEFAULT '1' COMMENT 'Status: 1=enabled, 0=disabled',
|
||||||
|
`created_at` datetime NOT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime NOT NULL COMMENT 'Update Time',
|
||||||
|
`deleted_at` datetime DEFAULT NULL COMMENT 'Deletion Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_code` (`code`),
|
||||||
|
KEY `idx_deleted_at` (`deleted_at`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Redemption Code Table';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `redemption_record` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||||
|
`redemption_code_id` bigint NOT NULL DEFAULT '0' COMMENT 'Redemption Code Id',
|
||||||
|
`user_id` bigint NOT NULL DEFAULT '0' COMMENT 'User Id',
|
||||||
|
`subscribe_id` bigint NOT NULL DEFAULT '0' COMMENT 'Subscribe Id',
|
||||||
|
`unit_time` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'month' COMMENT 'Unit Time',
|
||||||
|
`quantity` bigint NOT NULL DEFAULT '1' COMMENT 'Quantity',
|
||||||
|
`redeemed_at` datetime NOT NULL COMMENT 'Redeemed Time',
|
||||||
|
`created_at` datetime NOT NULL COMMENT 'Creation Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_redemption_code_id` (`redemption_code_id`),
|
||||||
|
KEY `idx_user_id` (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Redemption Record Table';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `schema_migrations` (
|
||||||
|
`version` bigint NOT NULL,
|
||||||
|
`dirty` tinyint(1) NOT NULL,
|
||||||
|
PRIMARY KEY (`version`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `server` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Name',
|
||||||
|
`tags` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Tags',
|
||||||
|
`country` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Country',
|
||||||
|
`city` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'City',
|
||||||
|
`latitude` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'latitude',
|
||||||
|
`longitude` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'longitude',
|
||||||
|
`server_addr` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Address',
|
||||||
|
`relay_mode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'none' COMMENT 'Relay Mode',
|
||||||
|
`relay_node` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Relay Node',
|
||||||
|
`speed_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Speed Limit',
|
||||||
|
`traffic_ratio` decimal(4,2) NOT NULL DEFAULT '0.00' COMMENT 'Traffic Ratio',
|
||||||
|
`group_id` bigint DEFAULT NULL COMMENT 'Group ID',
|
||||||
|
`protocol` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Protocol',
|
||||||
|
`config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Config',
|
||||||
|
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Enabled',
|
||||||
|
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
|
||||||
|
`last_reported_at` datetime(3) DEFAULT NULL COMMENT 'Last Reported Time',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_group_id` (`group_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `servers` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Name',
|
||||||
|
`country` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Country',
|
||||||
|
`city` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'City',
|
||||||
|
`ratio` decimal(4,2) NOT NULL DEFAULT '0.00' COMMENT 'Traffic Ratio',
|
||||||
|
`address` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Server Address',
|
||||||
|
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
|
||||||
|
`protocols` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Protocol',
|
||||||
|
`last_reported_at` datetime(3) DEFAULT NULL COMMENT 'Last Reported Time',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
`longitude` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'longitude',
|
||||||
|
`latitude` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'latitude',
|
||||||
|
`longitude_center` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'longitude center',
|
||||||
|
`latitude_center` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'latitude center',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `subscribe` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Subscribe Name',
|
||||||
|
`language` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Language',
|
||||||
|
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Subscribe Description',
|
||||||
|
`unit_price` bigint NOT NULL DEFAULT '0' COMMENT 'Unit Price',
|
||||||
|
`unit_time` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Unit Time',
|
||||||
|
`discount` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Discount',
|
||||||
|
`replacement` bigint NOT NULL DEFAULT '0' COMMENT 'Replacement',
|
||||||
|
`inventory` bigint NOT NULL DEFAULT '0' COMMENT 'Inventory',
|
||||||
|
`traffic` bigint NOT NULL DEFAULT '0' COMMENT 'Traffic',
|
||||||
|
`speed_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Speed Limit',
|
||||||
|
`device_limit` bigint NOT NULL DEFAULT '0' COMMENT 'Device Limit',
|
||||||
|
`quota` bigint NOT NULL DEFAULT '0' COMMENT 'Quota',
|
||||||
|
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Show portal page',
|
||||||
|
`sell` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Sell',
|
||||||
|
`sort` bigint NOT NULL DEFAULT '0' COMMENT 'Sort',
|
||||||
|
`deduction_ratio` bigint DEFAULT '0' COMMENT 'Deduction Ratio',
|
||||||
|
`allow_deduction` tinyint(1) DEFAULT '1' COMMENT 'Allow deduction',
|
||||||
|
`reset_cycle` bigint DEFAULT '0' COMMENT 'Reset Cycle: 0: No Reset, 1: 1st, 2: Monthly, 3: Yearly',
|
||||||
|
`renewal_reset` tinyint(1) DEFAULT '0' COMMENT 'Renew Reset',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`show_original_price` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'display the original price: 0 not display, 1 display',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
`nodes` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node IDs',
|
||||||
|
`node_tags` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Node Tags',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `subscribe_application` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Application Name',
|
||||||
|
`icon` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Application Icon',
|
||||||
|
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Application Description',
|
||||||
|
`scheme` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Application Scheme',
|
||||||
|
`user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'User Agent',
|
||||||
|
`is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Default Application',
|
||||||
|
`subscribe_template` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Subscribe Template',
|
||||||
|
`output_format` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'yaml' COMMENT 'Output Format',
|
||||||
|
`download_link` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Download Link',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `system` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Category',
|
||||||
|
`key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Key Name',
|
||||||
|
`value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Key Value',
|
||||||
|
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Type',
|
||||||
|
`desc` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Description',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_system_key` (`key`),
|
||||||
|
KEY `index_key` (`key`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `system_logs` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Log Type: 1: Email Message 2: Mobile Message 3: Subscribe 4: Subscribe Traffic 5: Server Traffic 6: Login 7: Register 8: Balance 9: Commission 10: Reset Subscribe 11: Gift',
|
||||||
|
`date` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Log Date',
|
||||||
|
`object_id` bigint NOT NULL DEFAULT '0' COMMENT 'Object ID',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Log Content',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_type` (`type`),
|
||||||
|
KEY `idx_object_id` (`object_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `task` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||||
|
`type` tinyint NOT NULL COMMENT 'Task Type',
|
||||||
|
`scope` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Task Scope',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Task Content',
|
||||||
|
`status` tinyint NOT NULL DEFAULT '0' COMMENT 'Task Status: 0: Pending, 1: In Progress, 2: Completed, 3: Failed',
|
||||||
|
`errors` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Task Errors',
|
||||||
|
`total` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Total Number',
|
||||||
|
`current` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Current Number',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `ticket` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Title',
|
||||||
|
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Description',
|
||||||
|
`user_id` bigint NOT NULL DEFAULT '0' COMMENT 'UserId',
|
||||||
|
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Status',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `ticket_follow` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`ticket_id` bigint NOT NULL DEFAULT '0' COMMENT 'TicketId',
|
||||||
|
`from` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'From',
|
||||||
|
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Type: 1 text, 2 image',
|
||||||
|
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'Content',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Create Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `traffic_log` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`server_id` bigint NOT NULL COMMENT 'Server ID',
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`subscribe_id` bigint NOT NULL COMMENT 'Subscription ID',
|
||||||
|
`download` bigint DEFAULT '0' COMMENT 'Download Traffic',
|
||||||
|
`upload` bigint DEFAULT '0' COMMENT 'Upload Traffic',
|
||||||
|
`timestamp` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Traffic Log Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_subscribe_id` (`subscribe_id`),
|
||||||
|
KEY `idx_server_id` (`server_id`),
|
||||||
|
KEY `idx_user_id` (`user_id`),
|
||||||
|
KEY `idx_traffic_log_time_user_sub` (`timestamp`,`user_id`,`subscribe_id`),
|
||||||
|
KEY `idx_timestamp` (`timestamp`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `user` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'User Password',
|
||||||
|
`algo` varchar(20) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'default' COMMENT 'Encryption Algorithm',
|
||||||
|
`salt` varchar(20) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'default' COMMENT 'Password Salt',
|
||||||
|
`avatar` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 'User Avatar',
|
||||||
|
`balance` bigint DEFAULT '0' COMMENT 'User Balance',
|
||||||
|
`telegram` bigint DEFAULT NULL COMMENT 'Telegram Account',
|
||||||
|
`refer_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Referral Code',
|
||||||
|
`referer_id` bigint DEFAULT NULL COMMENT 'Referrer ID',
|
||||||
|
`commission` bigint DEFAULT '0' COMMENT 'Commission',
|
||||||
|
`referral_percentage` tinyint unsigned NOT NULL DEFAULT '0' COMMENT 'Referral Percentage',
|
||||||
|
`only_first_purchase` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Only First Purchase',
|
||||||
|
`gift_amount` bigint DEFAULT '0' COMMENT 'User Gift Amount',
|
||||||
|
`enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Is Account Enabled',
|
||||||
|
`is_admin` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Admin',
|
||||||
|
`valid_email` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Email Verified',
|
||||||
|
`enable_email_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Email Notifications',
|
||||||
|
`enable_telegram_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Telegram Notifications',
|
||||||
|
`enable_balance_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Balance Change Notifications',
|
||||||
|
`enable_login_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Login Notifications',
|
||||||
|
`enable_subscribe_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Subscription Notifications',
|
||||||
|
`enable_trade_notify` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Enable Trade Notifications',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`rules` text COLLATE utf8mb4_general_ci COMMENT 'User rules for subscription',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
`deleted_at` datetime(3) DEFAULT NULL COMMENT 'Deletion Time',
|
||||||
|
`is_del` bigint unsigned DEFAULT NULL COMMENT '1: Normal 0: Deleted',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_referer` (`referer_id`),
|
||||||
|
KEY `idx_refer_code` (`refer_code`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `user_auth_methods` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`auth_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Auth Type 1: apple 2: google 3: github 4: facebook 5: telegram 6: email 7: phone',
|
||||||
|
`auth_identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Auth Identifier',
|
||||||
|
`verified` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is Verified',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `idx_auth_identifier` (`auth_identifier`),
|
||||||
|
KEY `idx_user_id` (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `user_device` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`subscribe_id` bigint DEFAULT NULL COMMENT 'Subscribe ID',
|
||||||
|
`ip` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Ip.',
|
||||||
|
`Identifier` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device Identifier.',
|
||||||
|
`short_code` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Short Code',
|
||||||
|
`user_agent` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Device User Agent.',
|
||||||
|
`online` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Online',
|
||||||
|
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'EnableDeviceNumber',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_user_id` (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `user_device_online_record` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`identifier` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Device Identifier',
|
||||||
|
`online_time` datetime DEFAULT NULL COMMENT 'Online Time',
|
||||||
|
`offline_time` datetime DEFAULT NULL COMMENT 'Offline Time',
|
||||||
|
`online_seconds` bigint DEFAULT NULL COMMENT 'Offline Seconds',
|
||||||
|
`duration_days` bigint DEFAULT NULL COMMENT 'Duration Days',
|
||||||
|
`created_at` datetime DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `user_subscribe` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`order_id` bigint NOT NULL COMMENT 'Order ID',
|
||||||
|
`subscribe_id` bigint NOT NULL COMMENT 'Subscription ID',
|
||||||
|
`start_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Subscription Start Time',
|
||||||
|
`expire_time` datetime(3) DEFAULT NULL COMMENT 'Subscription Expire Time',
|
||||||
|
`finished_at` datetime DEFAULT NULL COMMENT 'Subscribe Finished Time',
|
||||||
|
`traffic` bigint DEFAULT '0' COMMENT 'Traffic',
|
||||||
|
`download` bigint DEFAULT '0' COMMENT 'Download Traffic',
|
||||||
|
`upload` bigint DEFAULT '0' COMMENT 'Upload Traffic',
|
||||||
|
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'Token',
|
||||||
|
`uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'UUID',
|
||||||
|
`status` tinyint(1) DEFAULT '0' COMMENT 'Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired 4: Deducted',
|
||||||
|
`note` varchar(500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'User note for subscription',
|
||||||
|
`created_at` datetime(3) DEFAULT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime(3) DEFAULT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uni_user_subscribe_token` (`token`),
|
||||||
|
UNIQUE KEY `uni_user_subscribe_uuid` (`uuid`),
|
||||||
|
KEY `idx_user_id` (`user_id`),
|
||||||
|
KEY `idx_order_id` (`order_id`),
|
||||||
|
KEY `idx_subscribe_id` (`subscribe_id`),
|
||||||
|
KEY `idx_token` (`token`),
|
||||||
|
KEY `idx_uuid` (`uuid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `withdrawals` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||||
|
`user_id` bigint NOT NULL COMMENT 'User ID',
|
||||||
|
`amount` bigint NOT NULL COMMENT 'Withdrawal Amount',
|
||||||
|
`content` text COMMENT 'Withdrawal Content',
|
||||||
|
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Withdrawal Status',
|
||||||
|
`reason` varchar(500) NOT NULL DEFAULT '' COMMENT 'Rejection Reason',
|
||||||
|
`created_at` datetime NOT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` datetime NOT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_user_id` (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
TRUNCATE TABLE `ads`;
|
||||||
|
TRUNCATE TABLE `announcement`;
|
||||||
|
TRUNCATE TABLE `auth_method`;
|
||||||
|
TRUNCATE TABLE `coupon`;
|
||||||
|
TRUNCATE TABLE `document`;
|
||||||
|
TRUNCATE TABLE `nodes`;
|
||||||
|
TRUNCATE TABLE `order`;
|
||||||
|
TRUNCATE TABLE `payment`;
|
||||||
|
TRUNCATE TABLE `redemption_code`;
|
||||||
|
TRUNCATE TABLE `redemption_record`;
|
||||||
|
TRUNCATE TABLE `server`;
|
||||||
|
TRUNCATE TABLE `servers`;
|
||||||
|
TRUNCATE TABLE `subscribe`;
|
||||||
|
TRUNCATE TABLE `subscribe_application`;
|
||||||
|
TRUNCATE TABLE `system`;
|
||||||
|
TRUNCATE TABLE `system_logs`;
|
||||||
|
TRUNCATE TABLE `task`;
|
||||||
|
TRUNCATE TABLE `ticket`;
|
||||||
|
TRUNCATE TABLE `ticket_follow`;
|
||||||
|
TRUNCATE TABLE `traffic_log`;
|
||||||
|
TRUNCATE TABLE `user`;
|
||||||
|
TRUNCATE TABLE `user_auth_methods`;
|
||||||
|
TRUNCATE TABLE `user_device`;
|
||||||
|
TRUNCATE TABLE `user_device_online_record`;
|
||||||
|
TRUNCATE TABLE `user_subscribe`;
|
||||||
|
TRUNCATE TABLE `withdrawals`;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
|||||||
-- PPanel Database Schema Rollback
|
|
||||||
-- Migration: 001_init
|
|
||||||
-- Description: Drop all tables from initial schema
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `user`;
|
|
||||||
|
|
||||||
-- TODO: Add remaining DROP TABLE statements (in reverse order of creation)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
-- PPanel Database Schema
|
|
||||||
-- Migration: 001_init
|
|
||||||
-- Description: Initial database schema
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- User Domain
|
|
||||||
-- ============================================================
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `user` (
|
|
||||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`email` varchar(255) DEFAULT NULL,
|
|
||||||
`telephone` varchar(32) DEFAULT NULL,
|
|
||||||
`password` varchar(255) NOT NULL DEFAULT '',
|
|
||||||
`refer_code` varchar(32) DEFAULT NULL,
|
|
||||||
`refer_id` bigint unsigned DEFAULT 0,
|
|
||||||
`avatar` varchar(512) DEFAULT '',
|
|
||||||
`balance` bigint DEFAULT 0,
|
|
||||||
`commission` bigint DEFAULT 0,
|
|
||||||
`gift_amount` bigint DEFAULT 0,
|
|
||||||
`is_admin` tinyint(1) NOT NULL DEFAULT 0,
|
|
||||||
`is_staff` tinyint(1) NOT NULL DEFAULT 0,
|
|
||||||
`enable` tinyint(1) NOT NULL DEFAULT 1,
|
|
||||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE KEY `idx_email` (`email`),
|
|
||||||
UNIQUE KEY `idx_telephone` (`telephone`),
|
|
||||||
UNIQUE KEY `idx_refer_code` (`refer_code`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- TODO: Add remaining tables
|
|
||||||
-- user_auth_methods, user_device, user_subscribe, user_balance_log,
|
|
||||||
-- user_commission_log, user_subscribe_log, user_login_log,
|
|
||||||
-- user_device_online_record, user_gift_amount_log, user_reset_subscribe_log
|
|
||||||
-- order, coupon, ticket, ticket_follow
|
|
||||||
-- payment, announcement, document
|
|
||||||
-- system, server, server_group, server_rule_group
|
|
||||||
-- subscribe, subscribe_group, subscribe_type, subscribe_application
|
|
||||||
-- ads, application, application_config, application_version
|
|
||||||
-- traffic_log, system_logs
|
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* 设备登录接口测试脚本
|
||||||
|
*
|
||||||
|
* 复现服务端的加密与签名逻辑:
|
||||||
|
* - AES-CBC 加密: key=SHA256(secret)[:32], iv=SHA256(md5hex(nonce)+secret)[:16]
|
||||||
|
* - 签名: HMAC-SHA256(appSecret, METHOD\nPATH\nQUERY\nBODY_SHA256\nAppId\nTimestamp\nNonce)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import crypto from 'node:crypto';
|
||||||
|
import https from 'node:https';
|
||||||
|
import http from 'node:http';
|
||||||
|
|
||||||
|
// ─── 配置(对应 api-dev.yaml)─────────────────────────────────────────────────
|
||||||
|
const CONFIG = {
|
||||||
|
baseUrl: 'http://127.0.0.1:8080', // 本地测试改这里;生产改为 https://tapi.hifast.biz
|
||||||
|
appId: 'android-client',
|
||||||
|
appSecret: 'uB4G,XxL2{7b}', // AppSignature.AppSecrets[appId]
|
||||||
|
securitySecret: 'uB4G,XxL2{7b', // Security.SecuritySecret(注意少一个 })
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── AES-CBC 加密工具(与 pkg/cryptox/aes.go 对齐)─────────────────────────────
|
||||||
|
|
||||||
|
function generateKey(secret) {
|
||||||
|
return crypto.createHash('sha256').update(secret).digest().slice(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateIv(nonce, secret) {
|
||||||
|
const md5hex = crypto.createHash('md5').update(nonce).digest('hex');
|
||||||
|
return crypto.createHash('sha256').update(md5hex + secret).digest().slice(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function aesEncrypt(plaintext, secret) {
|
||||||
|
const nonce = crypto.randomBytes(8).toString('hex'); // 16位 hex,对应 Go 的 fmt.Sprintf("%x", UnixNano)
|
||||||
|
const key = generateKey(secret);
|
||||||
|
const iv = generateIv(nonce, secret);
|
||||||
|
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
||||||
|
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
||||||
|
return {
|
||||||
|
data: encrypted.toString('base64'),
|
||||||
|
time: nonce,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── 签名工具(与 pkg/signature/canonical.go 对齐)────────────────────────────
|
||||||
|
|
||||||
|
function sha256Hex(data) {
|
||||||
|
return crypto.createHash('sha256').update(data).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStringToSign(method, path, rawQuery, bodyBytes, appId, timestamp, nonce) {
|
||||||
|
// 规范化 query(按 key 字母排序)
|
||||||
|
let canonical = '';
|
||||||
|
if (rawQuery) {
|
||||||
|
const params = new URLSearchParams(rawQuery);
|
||||||
|
const sorted = [...params.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
||||||
|
canonical = sorted.map(([k, v]) => `${k}=${v}`).join('&');
|
||||||
|
}
|
||||||
|
const bodyHash = sha256Hex(bodyBytes);
|
||||||
|
return [method.toUpperCase(), path, canonical, bodyHash, appId, timestamp, nonce].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hmacSha256(secret, data) {
|
||||||
|
return crypto.createHmac('sha256', secret).update(data).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── 发送请求 ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function request(method, path, payload) {
|
||||||
|
const url = new URL(CONFIG.baseUrl + path);
|
||||||
|
|
||||||
|
const timestamp = String(Math.floor(Date.now() / 1000));
|
||||||
|
const nonce = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
// 1. AES 加密 body
|
||||||
|
const { data: encData, time: encTime } = aesEncrypt(JSON.stringify(payload), CONFIG.securitySecret);
|
||||||
|
const bodyObj = { data: encData, time: encTime };
|
||||||
|
const bodyStr = JSON.stringify(bodyObj);
|
||||||
|
const bodyBytes = Buffer.from(bodyStr, 'utf8');
|
||||||
|
|
||||||
|
// 2. 构造签名
|
||||||
|
const sts = buildStringToSign(method, url.pathname, url.search.slice(1), bodyBytes, CONFIG.appId, timestamp, nonce);
|
||||||
|
const sig = hmacSha256(CONFIG.appSecret, sts);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-App-Id': CONFIG.appId,
|
||||||
|
'X-Timestamp': timestamp,
|
||||||
|
'X-Nonce': nonce,
|
||||||
|
'X-Signature': sig,
|
||||||
|
'Login-Type': 'device',
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('\n📤 请求信息');
|
||||||
|
console.log(' URL:', url.toString());
|
||||||
|
console.log(' timestamp:', timestamp);
|
||||||
|
console.log(' nonce:', nonce);
|
||||||
|
console.log(' signature:', sig);
|
||||||
|
console.log(' 加密 nonce(time):', encTime);
|
||||||
|
console.log(' 签名原文:\n' + sts.split('\n').map(l => ' ' + l).join('\n'));
|
||||||
|
console.log(' body:', bodyStr);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const lib = url.protocol === 'https:' ? https : http;
|
||||||
|
const req = lib.request(url, { method, headers }, (res) => {
|
||||||
|
let raw = '';
|
||||||
|
res.on('data', chunk => raw += chunk);
|
||||||
|
res.on('end', () => {
|
||||||
|
console.log('\n📥 响应');
|
||||||
|
console.log(' status:', res.statusCode);
|
||||||
|
try {
|
||||||
|
console.log(' body:', JSON.stringify(JSON.parse(raw), null, 2));
|
||||||
|
} catch {
|
||||||
|
console.log(' body:', raw);
|
||||||
|
}
|
||||||
|
resolve({ status: res.statusCode, body: raw });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(bodyStr);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── 测试用例 ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
console.log('🔐 设备登录接口测试');
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
|
||||||
|
// 测试 1:正常设备登录
|
||||||
|
console.log('\n【TEST 1】正常设备登录');
|
||||||
|
await request('POST', '/api/v1/auth/login/device', {
|
||||||
|
identifier: 'test-device-001',
|
||||||
|
user_agent: 'TestClient/1.0 (Node.js test)',
|
||||||
|
short_code: '',
|
||||||
|
cf_token: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 测试 2:不同 identifier(新设备,应该自动注册)
|
||||||
|
console.log('\n【TEST 2】新设备首次登录');
|
||||||
|
await request('POST', '/api/v1/auth/login/device', {
|
||||||
|
identifier: `device-${Date.now()}`,
|
||||||
|
user_agent: 'TestClient/1.0 (Node.js test)',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
Reference in New Issue
Block a user