Compare commits
29 Commits
9dacd85a89
...
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 | |||
| cd7be35668 | |||
| e1896f677c |
@@ -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_USER: ${{ vars.SSH_USER }}
|
||||
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
||||
TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
|
||||
TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}
|
||||
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
||||
TG_CHAT_ID: "-4940243803"
|
||||
VERSION: ${{ github.sha }}
|
||||
BUILDTIME: ${{ github.event.head_commit.timestamp }}
|
||||
|
||||
@@ -27,11 +27,20 @@ jobs:
|
||||
# Job 1: 设置环境变量,供后续 jobs 共享
|
||||
# ============================================================
|
||||
prepare:
|
||||
runs-on: ario-server
|
||||
runs-on: zero-ppanel-server
|
||||
container:
|
||||
image: node:20
|
||||
strategy:
|
||||
matrix:
|
||||
# 只有node支持版本号别名
|
||||
node: ['20.15.1']
|
||||
outputs:
|
||||
docker_tag: ${{ steps.vars.outputs.docker_tag }}
|
||||
container_suffix: ${{ steps.vars.outputs.container_suffix }}
|
||||
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:
|
||||
- name: ⚙️ 计算部署变量
|
||||
id: vars
|
||||
@@ -54,16 +63,127 @@ jobs:
|
||||
;;
|
||||
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:
|
||||
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
|
||||
if: needs.prepare.outputs.has_changes == 'true' && contains(needs.prepare.outputs.changed_services, matrix.service.name)
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
service:
|
||||
- name: rpc-core
|
||||
dockerfile: deploy/Dockerfile.rpc-core
|
||||
image_name: ppanel-rpc-core
|
||||
- name: api
|
||||
dockerfile: deploy/Dockerfile.api
|
||||
image_name: ppanel-api
|
||||
@@ -84,65 +204,61 @@ jobs:
|
||||
- name: 📥 下载代码
|
||||
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: |
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -y
|
||||
apt-get install -y ca-certificates curl gnupg
|
||||
|
||||
# 等待 apt 锁释放
|
||||
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
|
||||
# !!! 在 node 容器中安装 docker-ce-cli !!!
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
||||
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
|
||||
https://download.docker.com/linux/debian \
|
||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||
> /etc/apt/sources.list.d/docker.list
|
||||
apt-get update -y -o Dpkg::Lock::Timeout=300
|
||||
apt-get install -y -o Dpkg::Lock::Timeout=300 docker-ce-cli docker-buildx-plugin
|
||||
apt-get update -y
|
||||
apt-get install -y docker-ce-cli # <<-- 确保 docker CLI 被安装
|
||||
|
||||
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: |
|
||||
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||||
IMAGE_BASE="${{ env.REPO }}/${{ matrix.service.image_name }}"
|
||||
set -e
|
||||
IMAGE_NAME="${{ matrix.service.image_name }}"
|
||||
DOCKERFILE="${{ matrix.service.dockerfile }}"
|
||||
DEPLOY_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||||
REPO="${{ env.REPO }}"
|
||||
|
||||
echo "构建服务: ${{ matrix.service.name }}"
|
||||
echo "镜像: ${IMAGE_BASE}"
|
||||
echo "标签: ${DOCKER_TAG} | ${{ env.VERSION }}"
|
||||
FULL_IMAGE_NAME="${REPO}/${IMAGE_NAME}:${DEPLOY_TAG}"
|
||||
|
||||
docker build \
|
||||
-f ${{ matrix.service.dockerfile }} \
|
||||
--platform linux/amd64 \
|
||||
--build-arg VERSION=${{ env.VERSION }} \
|
||||
--build-arg BUILDTIME=${{ env.BUILDTIME }} \
|
||||
-t ${IMAGE_BASE}:${{ env.VERSION }} \
|
||||
-t ${IMAGE_BASE}:${DOCKER_TAG} \
|
||||
.
|
||||
echo "🚀 开始构建镜像: ${FULL_IMAGE_NAME} 从 ${DOCKERFILE}"
|
||||
docker build -t "${FULL_IMAGE_NAME}" -f "${DOCKERFILE}" .
|
||||
|
||||
docker push ${IMAGE_BASE}:${{ env.VERSION }}
|
||||
docker push ${IMAGE_BASE}:${DOCKER_TAG}
|
||||
echo "⬆️ 推送镜像: ${FULL_IMAGE_NAME}"
|
||||
docker push "${FULL_IMAGE_NAME}"
|
||||
|
||||
echo "✅ ${{ matrix.service.name }} 镜像推送完成"
|
||||
|
||||
# ============================================================
|
||||
# Job 3: 部署到服务器
|
||||
# ============================================================
|
||||
deploy:
|
||||
runs-on: ario-server
|
||||
runs-on: zero-ppanel-server
|
||||
container: # <-- 新增:deploy job 也在 Node.js 容器中运行
|
||||
image: node:20.15.1
|
||||
needs: [prepare, build]
|
||||
# PR 不触发部署,只有直接推送才部署
|
||||
if: github.event_name == 'push'
|
||||
if: github.event_name == 'push' && needs.prepare.outputs.has_changes == 'true'
|
||||
|
||||
steps:
|
||||
- name: 📥 下载代码 (获取 docker-compose.cloud.yml)
|
||||
@@ -155,7 +271,7 @@ jobs:
|
||||
username: ${{ env.SSH_USER }}
|
||||
password: ${{ env.SSH_PASSWORD }}
|
||||
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 }}/"
|
||||
strip_components: 1
|
||||
|
||||
@@ -173,24 +289,32 @@ jobs:
|
||||
DEPLOY_PATH="${{ needs.prepare.outputs.deploy_path }}"
|
||||
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||||
REPO="${{ env.REPO }}"
|
||||
DEPLOY_SERVICES="${{ needs.prepare.outputs.deploy_services }}"
|
||||
|
||||
echo "部署目录: ${DEPLOY_PATH}"
|
||||
echo "镜像标签: ${DOCKER_TAG}"
|
||||
|
||||
cd ${DEPLOY_PATH}
|
||||
|
||||
# 写入环境变量供 docker-compose 使用
|
||||
cat > .env <<EOF
|
||||
PPANEL_TAG=${DOCKER_TAG}
|
||||
PPANEL_REPO=${REPO}
|
||||
EOF
|
||||
# 保留已有业务配置,仅更新镜像仓库和标签
|
||||
touch .env
|
||||
grep -q '^PPANEL_TAG=' .env \
|
||||
&& sed -i "s|^PPANEL_TAG=.*|PPANEL_TAG=${DOCKER_TAG}|" .env \
|
||||
|| 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 服务
|
||||
docker-compose -f docker-compose.cloud.yml up -d \
|
||||
ppanel-api ppanel-admin ppanel-node ppanel-queue ppanel-scheduler
|
||||
docker-compose -f docker-compose.cloud.yml up -d ${DEPLOY_SERVICES}
|
||||
|
||||
# 清理旧镜像
|
||||
docker image prune -f || true
|
||||
@@ -202,7 +326,7 @@ jobs:
|
||||
# Job 4: 通知
|
||||
# ============================================================
|
||||
notify:
|
||||
runs-on: ario-server
|
||||
runs-on: zero-ppanel-server
|
||||
needs: [prepare, build, deploy]
|
||||
if: always() && github.event_name == 'push'
|
||||
steps:
|
||||
@@ -212,7 +336,7 @@ jobs:
|
||||
token: ${{ env.TG_BOT_TOKEN }}
|
||||
to: ${{ env.TG_CHAT_ID }}
|
||||
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
|
||||
🌿 分支: ${{ github.ref_name }}
|
||||
@@ -221,6 +345,6 @@ jobs:
|
||||
👤 提交者: ${{ github.actor }}
|
||||
🕐 时间: ${{ github.event.head_commit.timestamp }}
|
||||
|
||||
构建: ${{ needs.build.result }} | 部署: ${{ needs.deploy.result }}
|
||||
${{ (needs.build.result != 'success' || needs.deploy.result != 'success') && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
|
||||
构建: ${{ 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.prepare.outputs.has_changes == 'true' && (needs.build.result != 'success' || needs.deploy.result != 'success')) && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
|
||||
parse_mode: Markdown
|
||||
|
||||
@@ -14,6 +14,8 @@ logs/
|
||||
# Local config overrides
|
||||
etc/*.local.yaml
|
||||
apps/*/etc/*.local.yaml
|
||||
deploy/.env
|
||||
goctl_tpl/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
@@ -34,3 +36,4 @@ tmp/
|
||||
# Claude
|
||||
CLAUDE.md
|
||||
.claude/
|
||||
.claude.json
|
||||
|
||||
@@ -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 \
|
||||
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)
|
||||
ENV ?= dev
|
||||
@@ -47,9 +47,9 @@ run-rpc-core:
|
||||
|
||||
# Code generation
|
||||
gen-api:
|
||||
cd apps/api && goctl api go -api api.api -dir . -style goZero
|
||||
cd apps/admin && goctl api go -api admin.api -dir . -style goZero
|
||||
cd apps/node && goctl api go -api node.api -dir . -style goZero
|
||||
cd apps/api && goctl api go -api api.api -dir . -style goZero -home ../../goctl_tpl
|
||||
cd apps/admin && goctl api go -api admin.api -dir . -style goZero -home ../../goctl_tpl
|
||||
cd apps/node && goctl api go -api node.api -dir . -style goZero -home ../../goctl_tpl
|
||||
# fix: admin routes.go 中 server 包名与参数名 server(*rest.Server) 冲突
|
||||
# 1. 若 goctl 生成了 server import 但没加别名,加上别名
|
||||
sed -i '' 's|"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"|serverhandler "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"|g' apps/admin/internal/handler/routes.go
|
||||
@@ -81,3 +81,36 @@ lint:
|
||||
|
||||
clean:
|
||||
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)
|
||||
|
||||
@@ -30,3 +30,17 @@ MySQL:
|
||||
Redis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b"
|
||||
web-client: "uB4G,XxL2{7b"
|
||||
ios-client: "uB4G,XxL2{7b"
|
||||
mac-client: "uB4G,XxL2{7b"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/admin/health
|
||||
|
||||
@@ -34,3 +34,18 @@ 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
|
||||
|
||||
@@ -32,3 +32,17 @@ MySQL:
|
||||
Redis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b"
|
||||
web-client: "uB4G,XxL2{7b"
|
||||
ios-client: "uB4G,XxL2{7b"
|
||||
mac-client: "uB4G,XxL2{7b"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/admin/health
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
package config
|
||||
|
||||
import "github.com/zeromicro/go-zero/rest"
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
@@ -11,4 +15,6 @@ type Config struct {
|
||||
AccessSecret string
|
||||
AccessExpire int64
|
||||
}
|
||||
CacheRedis redis.RedisConf
|
||||
AppSignature signature.SignatureConf
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateAnnouncementReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := announcement.NewCreateAnnouncementLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateAnnouncement(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func DeleteAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeleteAnnouncementReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := announcement.NewDeleteAnnouncementLogic(r.Context(), svcCtx)
|
||||
err := l.DeleteAnnouncement(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateAnnouncementReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := announcement.NewUpdateAnnouncementLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateAnnouncement(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewHealthLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Health()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/console"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetConsoleStatsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := console.NewGetConsoleStatsLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetConsoleStats()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/order"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetOrderListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminOrderListReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := order.NewGetOrderListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetOrderList(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/order"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateOrderStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateOrderStatusReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := order.NewUpdateOrderStatusLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateOrderStatus(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateServerReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := server.NewCreateServerLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateServer(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func DeleteServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeleteServerReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := server.NewDeleteServerLogic(r.Context(), svcCtx)
|
||||
err := l.DeleteServer(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetServerListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ServerListReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := server.NewGetServerListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetServerList(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateServerReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := server.NewUpdateServerLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateServer(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateSubscribeReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := subscribe.NewCreateSubscribeLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateSubscribe(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func DeleteSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeleteSubscribeReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := subscribe.NewDeleteSubscribeLogic(r.Context(), svcCtx)
|
||||
err := l.DeleteSubscribe(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetSubscribeListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminSubscribeListReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := subscribe.NewGetSubscribeListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetSubscribeList(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateSubscribeReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := subscribe.NewUpdateSubscribeLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateSubscribe(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetRegisterConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := system.NewGetRegisterConfigLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetRegisterConfig()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetSiteConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := system.NewGetSiteConfigLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetSiteConfig()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateRegisterConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateRegisterConfigReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := system.NewUpdateRegisterConfigLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateRegisterConfig(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateSiteConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateSiteConfigReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := system.NewUpdateSiteConfigLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateSiteConfig(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateTicketFollowHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminCreateTicketFollowReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewCreateTicketFollowLogic(r.Context(), svcCtx)
|
||||
err := l.CreateTicketFollow(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetTicketDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminTicketDetailReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewGetTicketDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetTicketDetail(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetTicketListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminTicketListReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewGetTicketListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetTicketList(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateTicketStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminUpdateTicketStatusReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewUpdateTicketStatusLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateTicketStatus(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func DeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminDeleteUserReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewDeleteUserLogic(r.Context(), svcCtx)
|
||||
err := l.DeleteUser(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetUserDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminUserDetailReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewGetUserDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetUserDetail(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.AdminUserListReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewGetUserListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetUserList(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type SignatureMiddleware struct {
|
||||
conf config.Config
|
||||
validator *signature.Validator
|
||||
}
|
||||
|
||||
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
|
||||
return &SignatureMiddleware{
|
||||
conf: c,
|
||||
validator: signature.NewValidator(c.AppSignature, store),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
appId := r.Header.Get("X-App-Id")
|
||||
if appId == "" {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := r.Header.Get("X-Timestamp")
|
||||
nonce := r.Header.Get("X-Nonce")
|
||||
sig := r.Header.Get("X-Signature")
|
||||
|
||||
if timestamp == "" || nonce == "" || sig == "" {
|
||||
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(xerr.SignatureMissing))
|
||||
return
|
||||
}
|
||||
|
||||
var bodyBytes []byte
|
||||
if r.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(r.Body)
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
}
|
||||
|
||||
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
|
||||
|
||||
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
|
||||
code := mapSignatureErr(err)
|
||||
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(code))
|
||||
return
|
||||
}
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func mapSignatureErr(err error) int {
|
||||
switch err {
|
||||
case signature.ErrSignatureMissing:
|
||||
return xerr.SignatureMissing
|
||||
case signature.ErrSignatureExpired:
|
||||
return xerr.SignatureExpired
|
||||
case signature.ErrSignatureReplay:
|
||||
return xerr.SignatureReplay
|
||||
default:
|
||||
return xerr.SignatureInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func buildErrResp(code int) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"code": code,
|
||||
"msg": xerr.MapErrMsg(code),
|
||||
"data": nil,
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,22 @@ package svc
|
||||
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/middleware"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
Config config.Config
|
||||
SignatureMiddleware *middleware.SignatureMiddleware
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
rds := redis.MustNewRedis(c.CacheRedis)
|
||||
nonceStore := signature.NewRedisNonceStore(rds)
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
Config: c,
|
||||
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||
|
||||
server := rest.MustNewServer(c.RestConf)
|
||||
defer server.Stop()
|
||||
|
||||
ctx := svc.NewServiceContext(c)
|
||||
server.Use(ctx.SignatureMiddleware.Handle)
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||
|
||||
+51
-17
@@ -6,26 +6,48 @@ info (
|
||||
|
||||
type (
|
||||
UserLoginReq {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
|
||||
LoginResp {
|
||||
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 {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
ReferCode string `json:"refer_code,optional"`
|
||||
}
|
||||
|
||||
AuthResp {
|
||||
Token string `json:"token"`
|
||||
Expire int64 `json:"expire"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Invite string `json:"invite,optional"`
|
||||
Code string `json:"code,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
|
||||
ResetPasswordReq {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Code string `json:"code,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -35,11 +57,23 @@ type (
|
||||
)
|
||||
service ppanel-api {
|
||||
@handler UserLoginHandler
|
||||
post /login (UserLoginReq) returns (AuthResp)
|
||||
post /login (UserLoginReq) returns (LoginResp)
|
||||
|
||||
@handler UserRegisterHandler
|
||||
post /register (UserRegisterReq) returns (AuthResp)
|
||||
post /register (UserRegisterReq) returns (LoginResp)
|
||||
|
||||
@handler ResetPasswordHandler
|
||||
post /reset_password (ResetPasswordReq)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,27 @@ Redis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b}"
|
||||
web-client: "uB4G,XxL2{7b}"
|
||||
ios-client: "uB4G,XxL2{7b}"
|
||||
mac-client: "uB4G,XxL2{7b}"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/health
|
||||
|
||||
Security:
|
||||
Enable: true
|
||||
SecuritySecret: "uB4G,XxL2{7b"
|
||||
|
||||
Device:
|
||||
Enable: true
|
||||
|
||||
Asynq:
|
||||
Addr: 127.0.0.1:6379
|
||||
|
||||
|
||||
@@ -35,6 +35,28 @@ Redis:
|
||||
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}"
|
||||
|
||||
@@ -33,5 +33,23 @@ Redis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b"
|
||||
web-client: "uB4G,XxL2{7b"
|
||||
ios-client: "uB4G,XxL2{7b"
|
||||
mac-client: "uB4G,XxL2{7b"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/health
|
||||
|
||||
Security:
|
||||
Enable: true
|
||||
SecuritySecret: "uB4G,XxL2{7b"
|
||||
|
||||
Asynq:
|
||||
Addr: redis:6379
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
)
|
||||
@@ -14,5 +16,14 @@ type Config struct {
|
||||
AccessSecret string
|
||||
AccessExpire int64
|
||||
}
|
||||
CoreRpc zrpc.RpcClientConf
|
||||
CoreRpc zrpc.RpcClientConf
|
||||
CacheRedis redis.RedisConf
|
||||
AppSignature signature.SignatureConf
|
||||
Security struct {
|
||||
Enable bool
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"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/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func ResetPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ResetPasswordReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := auth.NewResetPasswordLogic(r.Context(), svcCtx)
|
||||
err := l.ResetPassword(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
resp, err := l.ResetPassword(&req)
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"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/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UserLoginReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := auth.NewUserLoginLogic(r.Context(), svcCtx)
|
||||
resp, err := l.UserLogin(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"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/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UserRegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UserRegisterReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := auth.NewUserRegisterLogic(r.Context(), svcCtx)
|
||||
resp, err := l.UserRegister(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetAnnouncementListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetAnnouncementListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetAnnouncementList()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetAvailablePaymentMethodsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetAvailablePaymentMethodsLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetAvailablePaymentMethods()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetDocumentDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DocumentDetailReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := common.NewGetDocumentDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetDocumentDetail(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetDocumentListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetDocumentListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetDocumentList()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetGlobalConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetGlobalConfigLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetGlobalConfig()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetSubscribeGroupListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetSubscribeGroupListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetSubscribeGroupList()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetSubscribeListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewGetSubscribeListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetSubscribeList()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewHealthLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Health()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func SendEmailCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.SendEmailCodeReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := common.NewSendEmailCodeLogic(r.Context(), svcCtx)
|
||||
err := l.SendEmailCode(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CloseOrderHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CloseOrderReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := order.NewCloseOrderLogic(r.Context(), svcCtx)
|
||||
err := l.CloseOrder(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateOrderHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateOrderReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := order.NewCreateOrderLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateOrder(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetOrderDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.OrderDetailReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := order.NewGetOrderDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetOrderDetail(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,28 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/reset_password",
|
||||
Path: "/reset",
|
||||
Handler: auth.ResetPasswordHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
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(
|
||||
[]rest.Route{
|
||||
{
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateTicketFollowHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateTicketFollowReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewCreateTicketFollowLogic(r.Context(), svcCtx)
|
||||
err := l.CreateTicketFollow(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func CreateTicketHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateTicketReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewCreateTicketLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateTicket(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetTicketDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.TicketDetailReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := ticket.NewGetTicketDetailLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetTicketDetail(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetUserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := user.NewGetUserInfoLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetUserInfo()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetUserSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := user.NewGetUserSubscribeLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetUserSubscribe()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func UpdateUserPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdatePasswordReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := user.NewUpdateUserPasswordLogic(r.Context(), svcCtx)
|
||||
err := l.UpdateUserPassword(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -26,8 +26,8 @@ func NewResetPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Res
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordReq) error {
|
||||
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordReq) (resp *types.LoginResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLog
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UserLoginLogic) UserLogin(req *types.UserLoginReq) (resp *types.AuthResp, err error) {
|
||||
func (l *UserLoginLogic) UserLogin(req *types.UserLoginReq) (resp *types.LoginResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *User
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterReq) (resp *types.AuthResp, err error) {
|
||||
func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterReq) (resp *types.LoginResp, err error) {
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
return
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"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/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
@@ -28,17 +27,7 @@ func NewHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthLogi
|
||||
}
|
||||
|
||||
func (l *HealthLogic) Health() (resp *types.HealthResp, err error) {
|
||||
// 调用 RPC 进行 Ping 测试,这能触发全链路追踪 (API -> RPC)
|
||||
rpcResp, err := l.svcCtx.CoreRpc.Ping(l.ctx, &core.Empty{})
|
||||
// todo: add your logic here and delete this line
|
||||
|
||||
status := "ok"
|
||||
if err != nil {
|
||||
status = "rpc_error: " + err.Error()
|
||||
} else if rpcResp.Msg != "" {
|
||||
status = rpcResp.Msg
|
||||
}
|
||||
|
||||
return &types.HealthResp{
|
||||
Status: status,
|
||||
}, nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/cryptox"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type DecryptMiddleware struct {
|
||||
conf config.Config
|
||||
}
|
||||
|
||||
func NewDecryptMiddleware(c config.Config) *DecryptMiddleware {
|
||||
return &DecryptMiddleware{conf: c}
|
||||
}
|
||||
|
||||
func (m *DecryptMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !m.conf.Security.Enable {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Header.Get("Login-Type") != "device" {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
secret := m.conf.Security.SecuritySecret
|
||||
rw := newEncryptResponseWriter(w, secret)
|
||||
|
||||
// 解密 GET query
|
||||
query := r.URL.Query()
|
||||
dataStr := query.Get("data")
|
||||
timeStr := query.Get("time")
|
||||
if dataStr != "" && timeStr != "" {
|
||||
if plain, err := cryptox.Decrypt(dataStr, secret, timeStr); err == nil {
|
||||
params := map[string]interface{}{}
|
||||
if json.Unmarshal(plain, ¶ms) == nil {
|
||||
for k, v := range params {
|
||||
query.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
query.Del("data")
|
||||
query.Del("time")
|
||||
rawQuery := query.Encode()
|
||||
if strings.Contains(r.RequestURI, "?") {
|
||||
r.RequestURI = r.RequestURI[:strings.Index(r.RequestURI, "?")] + "?" + rawQuery
|
||||
}
|
||||
r.URL.RawQuery = rawQuery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解密 POST body
|
||||
if r.Body != nil {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil || len(body) == 0 {
|
||||
// body 为空或读取失败,直接放行
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
next(rw, r)
|
||||
rw.flush()
|
||||
return
|
||||
}
|
||||
|
||||
var envelope struct {
|
||||
Data string `json:"data"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &envelope); err != nil || envelope.Data == "" {
|
||||
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
|
||||
return
|
||||
}
|
||||
|
||||
plain, err := cryptox.Decrypt(envelope.Data, secret, envelope.Time)
|
||||
if err != nil {
|
||||
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
|
||||
return
|
||||
}
|
||||
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)
|
||||
rw.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// encryptResponseWriter 拦截响应,加密 data 字段
|
||||
type encryptResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
secret string
|
||||
status int
|
||||
}
|
||||
|
||||
func newEncryptResponseWriter(w http.ResponseWriter, secret string) *encryptResponseWriter {
|
||||
return &encryptResponseWriter{
|
||||
ResponseWriter: w,
|
||||
body: new(bytes.Buffer),
|
||||
secret: secret,
|
||||
status: http.StatusOK,
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *encryptResponseWriter) WriteHeader(code int) {
|
||||
rw.status = code
|
||||
}
|
||||
|
||||
func (rw *encryptResponseWriter) Write(data []byte) (int, error) {
|
||||
return rw.body.Write(data)
|
||||
}
|
||||
|
||||
func (rw *encryptResponseWriter) WriteString(s string) (int, error) {
|
||||
return rw.body.WriteString(s)
|
||||
}
|
||||
|
||||
func (rw *encryptResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return rw.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
func (rw *encryptResponseWriter) flush() {
|
||||
buf := rw.body.Bytes()
|
||||
out := buf
|
||||
|
||||
// 尝试加密 data 字段
|
||||
params := map[string]interface{}{}
|
||||
if err := json.Unmarshal(buf, ¶ms); err == nil {
|
||||
if data := params["data"]; data != nil {
|
||||
var jsonData []byte
|
||||
if str, ok := data.(string); ok {
|
||||
jsonData = []byte(str)
|
||||
} else {
|
||||
jsonData, _ = json.Marshal(data)
|
||||
}
|
||||
if dataB64, nonce, err := cryptox.Encrypt(jsonData, rw.secret); err == nil {
|
||||
params["data"] = map[string]interface{}{
|
||||
"data": dataB64,
|
||||
"time": nonce,
|
||||
}
|
||||
if enc, err := json.Marshal(params); err == nil {
|
||||
out = enc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rw.ResponseWriter.WriteHeader(rw.status)
|
||||
rw.ResponseWriter.Write(out)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type SignatureMiddleware struct {
|
||||
conf config.Config
|
||||
validator *signature.Validator
|
||||
}
|
||||
|
||||
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
|
||||
return &SignatureMiddleware{
|
||||
conf: c,
|
||||
validator: signature.NewValidator(c.AppSignature, store),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
appId := r.Header.Get("X-App-Id")
|
||||
// X-App-Id 为空,提示非法访问
|
||||
if appId == "" {
|
||||
httpx.Error(w, xerr.NewErrCode(xerr.InvalidAccess))
|
||||
return
|
||||
}
|
||||
|
||||
// SkipPrefixes 白名单
|
||||
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := r.Header.Get("X-Timestamp")
|
||||
nonce := r.Header.Get("X-Nonce")
|
||||
sig := r.Header.Get("X-Signature")
|
||||
|
||||
if timestamp == "" || nonce == "" || sig == "" {
|
||||
httpx.Error(w, xerr.NewErrCode(xerr.SignatureMissing))
|
||||
return
|
||||
}
|
||||
|
||||
// 读取 body(签名对原始 body bytes 计算)
|
||||
var bodyBytes []byte
|
||||
if r.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(r.Body)
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
}
|
||||
|
||||
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
|
||||
|
||||
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
|
||||
code := mapSignatureErr(err)
|
||||
httpx.Error(w, xerr.NewErrCode(code))
|
||||
return
|
||||
}
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func mapSignatureErr(err error) int {
|
||||
switch err {
|
||||
case signature.ErrSignatureMissing:
|
||||
return xerr.SignatureMissing
|
||||
case signature.ErrSignatureExpired:
|
||||
return xerr.SignatureExpired
|
||||
case signature.ErrSignatureReplay:
|
||||
return xerr.SignatureReplay
|
||||
default:
|
||||
return xerr.SignatureInvalid
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,31 @@ package svc
|
||||
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreclient"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/middleware"
|
||||
coreClient "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreClient"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
"github.com/zeromicro/go-zero/zrpc"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
CoreRpc coreClient.Core
|
||||
Config config.Config
|
||||
CoreRpc coreClient.Core
|
||||
Redis *redis.Redis
|
||||
SignatureMiddleware *middleware.SignatureMiddleware
|
||||
DecryptMiddleware rest.Middleware
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
rds := redis.MustNewRedis(c.CacheRedis)
|
||||
nonceStore := signature.NewRedisNonceStore(rds)
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
|
||||
Config: c,
|
||||
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
|
||||
Redis: rds,
|
||||
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
||||
DecryptMiddleware: middleware.NewDecryptMiddleware(c).Handle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,6 @@ type AnnouncementResp struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
type AuthResp struct {
|
||||
Token string `json:"token"`
|
||||
Expire int64 `json:"expire"`
|
||||
}
|
||||
|
||||
type CloseOrderReq struct {
|
||||
OrderNo string `path:"order_no"`
|
||||
}
|
||||
@@ -36,6 +31,14 @@ type CreateTicketReq struct {
|
||||
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 {
|
||||
Id int64 `path:"id"`
|
||||
}
|
||||
@@ -63,6 +66,10 @@ type HealthResp struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type OrderDetailReq struct {
|
||||
OrderNo string `path:"order_no"`
|
||||
}
|
||||
@@ -84,9 +91,14 @@ type PaymentMethodResp struct {
|
||||
}
|
||||
|
||||
type ResetPasswordReq struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Code string `json:"code,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
|
||||
type SendEmailCodeReq struct {
|
||||
@@ -132,15 +144,25 @@ type UserInfoResp struct {
|
||||
}
|
||||
|
||||
type UserLoginReq struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
|
||||
type UserRegisterReq struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
ReferCode string `json:"refer_code,optional"`
|
||||
Identifier string `json:"identifier"`
|
||||
Email string `json:"email" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
Invite string `json:"invite,optional"`
|
||||
Code string `json:"code,optional"`
|
||||
IP string `header:"X-Original-Forwarded-For,optional"`
|
||||
UserAgent string `header:"User-Agent,optional"`
|
||||
LoginType string `header:"Login-Type,optional"`
|
||||
CfToken string `json:"cf_token,optional"`
|
||||
}
|
||||
|
||||
type UserSubscribeResp struct {
|
||||
|
||||
+7
-1
@@ -10,9 +10,11 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/handler"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/conf"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
var configFile = flag.String("f", "etc/api-dev.yaml", "the config file")
|
||||
@@ -21,14 +23,18 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||
|
||||
server := rest.MustNewServer(c.RestConf)
|
||||
defer server.Stop()
|
||||
|
||||
ctx := svc.NewServiceContext(c)
|
||||
server.Use(ctx.SignatureMiddleware.Handle)
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
// Registe global http error handler
|
||||
httpx.SetErrorHandler(result.ErrHandler)
|
||||
|
||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||
server.Start()
|
||||
}
|
||||
|
||||
@@ -28,3 +28,17 @@ MySQL:
|
||||
Redis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: 127.0.0.1:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b"
|
||||
web-client: "uB4G,XxL2{7b"
|
||||
ios-client: "uB4G,XxL2{7b"
|
||||
mac-client: "uB4G,XxL2{7b"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/node/health
|
||||
|
||||
@@ -32,3 +32,18 @@ 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
|
||||
|
||||
@@ -30,3 +30,17 @@ MySQL:
|
||||
Redis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
CacheRedis:
|
||||
Host: redis:6379
|
||||
Type: node
|
||||
|
||||
AppSignature:
|
||||
AppSecrets:
|
||||
android-client: "uB4G,XxL2{7b"
|
||||
web-client: "uB4G,XxL2{7b"
|
||||
ios-client: "uB4G,XxL2{7b"
|
||||
mac-client: "uB4G,XxL2{7b"
|
||||
ValidWindowSeconds: 300
|
||||
SkipPrefixes:
|
||||
- /api/v1/node/health
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
|
||||
package config
|
||||
|
||||
import "github.com/zeromicro/go-zero/rest"
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
NodeSecret string
|
||||
CacheRedis redis.RedisConf
|
||||
AppSignature signature.SignatureConf
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/common"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := common.NewHealthLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Health()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetServerConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := node.NewGetServerConfigLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetServerConfig()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import (
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func GetServerUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := node.NewGetServerUserListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetServerUserList()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
result.HttpResult(r, w, resp, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func PushOnlineUsersHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.PushOnlineUsersReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := node.NewPushOnlineUsersLogic(r.Context(), svcCtx)
|
||||
err := l.PushOnlineUsers(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func PushStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.PushStatusReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := node.NewPushStatusLogic(r.Context(), svcCtx)
|
||||
err := l.PushStatus(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,23 +9,19 @@ import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/result"
|
||||
)
|
||||
|
||||
func PushUserTrafficHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.PushUserTrafficReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
if err := result.Parse(r, &req); err != nil {
|
||||
result.HttpResult(r, w, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := node.NewPushUserTrafficLogic(r.Context(), svcCtx)
|
||||
err := l.PushUserTraffic(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
}
|
||||
result.HttpResult(r, w, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
type SignatureMiddleware struct {
|
||||
conf config.Config
|
||||
validator *signature.Validator
|
||||
}
|
||||
|
||||
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
|
||||
return &SignatureMiddleware{
|
||||
conf: c,
|
||||
validator: signature.NewValidator(c.AppSignature, store),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
appId := r.Header.Get("X-App-Id")
|
||||
if appId == "" {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
timestamp := r.Header.Get("X-Timestamp")
|
||||
nonce := r.Header.Get("X-Nonce")
|
||||
sig := r.Header.Get("X-Signature")
|
||||
|
||||
if timestamp == "" || nonce == "" || sig == "" {
|
||||
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(xerr.SignatureMissing))
|
||||
return
|
||||
}
|
||||
|
||||
var bodyBytes []byte
|
||||
if r.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(r.Body)
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
}
|
||||
|
||||
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
|
||||
|
||||
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
|
||||
code := mapSignatureErr(err)
|
||||
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(code))
|
||||
return
|
||||
}
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func mapSignatureErr(err error) int {
|
||||
switch err {
|
||||
case signature.ErrSignatureMissing:
|
||||
return xerr.SignatureMissing
|
||||
case signature.ErrSignatureExpired:
|
||||
return xerr.SignatureExpired
|
||||
case signature.ErrSignatureReplay:
|
||||
return xerr.SignatureReplay
|
||||
default:
|
||||
return xerr.SignatureInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func buildErrResp(code int) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"code": code,
|
||||
"msg": xerr.MapErrMsg(code),
|
||||
"data": nil,
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,24 @@ package svc
|
||||
import (
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/config"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/middleware"
|
||||
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
NodeAuthMiddleware rest.Middleware
|
||||
Config config.Config
|
||||
NodeAuthMiddleware rest.Middleware
|
||||
SignatureMiddleware *middleware.SignatureMiddleware
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
rds := redis.MustNewRedis(c.CacheRedis)
|
||||
nonceStore := signature.NewRedisNonceStore(rds)
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
NodeAuthMiddleware: middleware.NewNodeAuthMiddleware().Handle,
|
||||
Config: c,
|
||||
NodeAuthMiddleware: middleware.NewNodeAuthMiddleware().Handle,
|
||||
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||
|
||||
server := rest.MustNewServer(c.RestConf)
|
||||
defer server.Stop()
|
||||
|
||||
ctx := svc.NewServiceContext(c)
|
||||
server.Use(ctx.SignatureMiddleware.Handle)
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||
|
||||
logx.MustSetup(c.Log)
|
||||
if c.Telemetry.Name != "" {
|
||||
|
||||
@@ -22,7 +22,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
conf.MustLoad(*configFile, &c, conf.UseEnv())
|
||||
ctx := svc.NewServiceContext(c)
|
||||
|
||||
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
||||
|
||||
@@ -18,6 +18,7 @@ message BasicResponse {
|
||||
// ----------------------------------------------------------------------------
|
||||
message GetUserInfoReq {
|
||||
int64 id = 1;
|
||||
string email = 2; // 用于根据邮箱查询用户
|
||||
}
|
||||
|
||||
message GetUserInfoResp {
|
||||
@@ -25,6 +26,9 @@ message GetUserInfoResp {
|
||||
string email = 2;
|
||||
string role = 3;
|
||||
string uuid = 4;
|
||||
string password = 5; // 用于验证密码
|
||||
bool is_disabled = 6; // 用户是否被禁用
|
||||
bool is_deleted = 7; // 用户是否已被删除
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -41,6 +45,23 @@ message GetNodeInfoResp {
|
||||
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 服务接口
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -53,4 +74,7 @@ service Core {
|
||||
|
||||
// 节点相关
|
||||
rpc GetNodeInfo(GetNodeInfoReq) returns(GetNodeInfoResp);
|
||||
|
||||
// 设备登录
|
||||
rpc DeviceLogin(DeviceLoginReq) returns(DeviceLoginResp);
|
||||
}
|
||||
|
||||
+211
-14
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.4
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v4.25.2
|
||||
// source: core.proto
|
||||
|
||||
package core
|
||||
@@ -118,6 +118,7 @@ func (x *BasicResponse) GetMsg() string {
|
||||
type GetUserInfoReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` // 用于根据邮箱查询用户
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -159,12 +160,22 @@ func (x *GetUserInfoReq) GetId() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetUserInfoReq) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetUserInfoResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
|
||||
Uuid string `protobuf:"bytes,4,opt,name=uuid,proto3" json:"uuid,omitempty"`
|
||||
Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` // 用于验证密码
|
||||
IsDisabled bool `protobuf:"varint,6,opt,name=is_disabled,json=isDisabled,proto3" json:"is_disabled,omitempty"` // 用户是否被禁用
|
||||
IsDeleted bool `protobuf:"varint,7,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"` // 用户是否已被删除
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -227,6 +238,27 @@ func (x *GetUserInfoResp) GetUuid() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserInfoResp) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserInfoResp) GetIsDisabled() bool {
|
||||
if x != nil {
|
||||
return x.IsDisabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *GetUserInfoResp) GetIsDeleted() bool {
|
||||
if x != nil {
|
||||
return x.IsDeleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Node 服务定义
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -342,6 +374,145 @@ func (x *GetNodeInfoResp) GetStatus() string {
|
||||
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
|
||||
|
||||
const file_core_proto_rawDesc = "" +
|
||||
@@ -351,25 +522,47 @@ const file_core_proto_rawDesc = "" +
|
||||
"\x05Empty\"5\n" +
|
||||
"\rBasicResponse\x12\x12\n" +
|
||||
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x10\n" +
|
||||
"\x03msg\x18\x02 \x01(\tR\x03msg\" \n" +
|
||||
"\x03msg\x18\x02 \x01(\tR\x03msg\"6\n" +
|
||||
"\x0eGetUserInfoReq\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\"_\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x14\n" +
|
||||
"\x05email\x18\x02 \x01(\tR\x05email\"\xbb\x01\n" +
|
||||
"\x0fGetUserInfoResp\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x14\n" +
|
||||
"\x05email\x18\x02 \x01(\tR\x05email\x12\x12\n" +
|
||||
"\x04role\x18\x03 \x01(\tR\x04role\x12\x12\n" +
|
||||
"\x04uuid\x18\x04 \x01(\tR\x04uuid\" \n" +
|
||||
"\x04uuid\x18\x04 \x01(\tR\x04uuid\x12\x1a\n" +
|
||||
"\bpassword\x18\x05 \x01(\tR\bpassword\x12\x1f\n" +
|
||||
"\vis_disabled\x18\x06 \x01(\bR\n" +
|
||||
"isDisabled\x12\x1d\n" +
|
||||
"\n" +
|
||||
"is_deleted\x18\a \x01(\bR\tisDeleted\" \n" +
|
||||
"\x0eGetNodeInfoReq\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\"e\n" +
|
||||
"\x0fGetNodeInfoResp\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\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" +
|
||||
"\x04Ping\x12\v.core.Empty\x1a\x13.core.BasicResponse\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 (
|
||||
file_core_proto_rawDescOnce sync.Once
|
||||
@@ -383,7 +576,7 @@ func file_core_proto_rawDescGZIP() []byte {
|
||||
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{
|
||||
(*Empty)(nil), // 0: core.Empty
|
||||
(*BasicResponse)(nil), // 1: core.BasicResponse
|
||||
@@ -391,16 +584,20 @@ var file_core_proto_goTypes = []any{
|
||||
(*GetUserInfoResp)(nil), // 3: core.GetUserInfoResp
|
||||
(*GetNodeInfoReq)(nil), // 4: core.GetNodeInfoReq
|
||||
(*GetNodeInfoResp)(nil), // 5: core.GetNodeInfoResp
|
||||
(*DeviceLoginReq)(nil), // 6: core.DeviceLoginReq
|
||||
(*DeviceLoginResp)(nil), // 7: core.DeviceLoginResp
|
||||
}
|
||||
var file_core_proto_depIdxs = []int32{
|
||||
0, // 0: core.Core.Ping:input_type -> core.Empty
|
||||
2, // 1: core.Core.GetUserInfo:input_type -> core.GetUserInfoReq
|
||||
4, // 2: core.Core.GetNodeInfo:input_type -> core.GetNodeInfoReq
|
||||
1, // 3: core.Core.Ping:output_type -> core.BasicResponse
|
||||
3, // 4: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
|
||||
5, // 5: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
|
||||
3, // [3:6] is the sub-list for method output_type
|
||||
0, // [0:3] is the sub-list for method input_type
|
||||
6, // 3: core.Core.DeviceLogin:input_type -> core.DeviceLoginReq
|
||||
1, // 4: core.Core.Ping:output_type -> core.BasicResponse
|
||||
3, // 5: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
|
||||
5, // 6: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
|
||||
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 extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
@@ -417,7 +614,7 @@ func file_core_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_proto_rawDesc), len(file_core_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc v6.33.4
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v4.25.2
|
||||
// source: core.proto
|
||||
|
||||
package core
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
Core_Ping_FullMethodName = "/core.Core/Ping"
|
||||
Core_GetUserInfo_FullMethodName = "/core.Core/GetUserInfo"
|
||||
Core_GetNodeInfo_FullMethodName = "/core.Core/GetNodeInfo"
|
||||
Core_DeviceLogin_FullMethodName = "/core.Core/DeviceLogin"
|
||||
)
|
||||
|
||||
// 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)
|
||||
// 节点相关
|
||||
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 {
|
||||
@@ -78,6 +81,16 @@ func (c *coreClient) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts .
|
||||
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.
|
||||
// All implementations must embed UnimplementedCoreServer
|
||||
// for forward compatibility.
|
||||
@@ -92,6 +105,8 @@ type CoreServer interface {
|
||||
GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error)
|
||||
// 节点相关
|
||||
GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error)
|
||||
// 设备登录
|
||||
DeviceLogin(context.Context, *DeviceLoginReq) (*DeviceLoginResp, error)
|
||||
mustEmbedUnimplementedCoreServer()
|
||||
}
|
||||
|
||||
@@ -103,13 +118,16 @@ type CoreServer interface {
|
||||
type UnimplementedCoreServer struct{}
|
||||
|
||||
func (UnimplementedCoreServer) Ping(context.Context, *Empty) (*BasicResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Ping not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (UnimplementedCoreServer) GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetUserInfo not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetUserInfo not implemented")
|
||||
}
|
||||
func (UnimplementedCoreServer) GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error) {
|
||||
return nil, status.Error(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) testEmbeddedByValue() {}
|
||||
@@ -122,7 +140,7 @@ type UnsafeCoreServer interface {
|
||||
}
|
||||
|
||||
func RegisterCoreServer(s grpc.ServiceRegistrar, srv CoreServer) {
|
||||
// If the following call panics, it indicates UnimplementedCoreServer was
|
||||
// If the following call pancis, it indicates UnimplementedCoreServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
@@ -186,6 +204,24 @@ func _Core_GetNodeInfo_Handler(srv interface{}, ctx context.Context, dec func(in
|
||||
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.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -205,6 +241,10 @@ var Core_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetNodeInfo",
|
||||
Handler: _Core_GetNodeInfo_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeviceLogin",
|
||||
Handler: _Core_DeviceLogin_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "core.proto",
|
||||
|
||||
@@ -14,12 +14,14 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
BasicResponse = core.BasicResponse
|
||||
Empty = core.Empty
|
||||
GetNodeInfoReq = core.GetNodeInfoReq
|
||||
GetNodeInfoResp = core.GetNodeInfoResp
|
||||
GetUserInfoReq = core.GetUserInfoReq
|
||||
GetUserInfoResp = core.GetUserInfoResp
|
||||
BasicResponse = core.BasicResponse
|
||||
DeviceLoginReq = core.DeviceLoginReq
|
||||
DeviceLoginResp = core.DeviceLoginResp
|
||||
Empty = core.Empty
|
||||
GetNodeInfoReq = core.GetNodeInfoReq
|
||||
GetNodeInfoResp = core.GetNodeInfoResp
|
||||
GetUserInfoReq = core.GetUserInfoReq
|
||||
GetUserInfoResp = core.GetUserInfoResp
|
||||
|
||||
Core interface {
|
||||
// Ping 检查服务健康状态
|
||||
@@ -28,6 +30,8 @@ type (
|
||||
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, 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 {
|
||||
@@ -58,3 +62,9 @@ func (m *defaultCore) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts
|
||||
client := core.NewCoreClient(m.cli.Conn())
|
||||
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,6 @@
|
||||
Name: core.rpc
|
||||
ListenOn: 0.0.0.0:8080
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 127.0.0.1:2379
|
||||
Key: core.rpc
|
||||
@@ -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
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
|
||||
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/svc"
|
||||
@@ -25,7 +26,48 @@ func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs
|
||||
|
||||
// 用户相关
|
||||
func (l *GetUserInfoLogic) GetUserInfo(in *core.GetUserInfoReq) (*core.GetUserInfoResp, error) {
|
||||
// todo: add your logic here and delete this line
|
||||
// 暂不支持空参数查询
|
||||
if in.Email == "" && in.Id == 0 {
|
||||
return nil, errors.New("id or email is required")
|
||||
}
|
||||
|
||||
return &core.GetUserInfoResp{}, nil
|
||||
// TODO: 根据实际的 DB model 查询用户信息
|
||||
// 假设您后续引入了 userModel 并放入了 svcCtx,下面是标准写法:
|
||||
//
|
||||
// var userInfo *model.User
|
||||
// var err error
|
||||
// if in.Email != "" {
|
||||
// userInfo, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, in.Email)
|
||||
// } else {
|
||||
// userInfo, err = l.svcCtx.UserModel.FindOne(l.ctx, in.Id)
|
||||
// }
|
||||
//
|
||||
// if err != nil {
|
||||
// if errors.Is(err, sqlc.ErrNotFound) {
|
||||
// return &core.GetUserInfoResp{}, nil // 用户不存在,由业务层通过 Resp 的零值判断
|
||||
// }
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// =============== Mock 数据用于联调测试 ===============
|
||||
// 为了使您的 API 服务现在的登录重构直接能进行 Postman 联调,
|
||||
// 此处返回一个模拟存在的用户(您可以直接用这个邮箱及秘密进行登录尝试)。
|
||||
// Mock Email: admin@admin.com
|
||||
// Mock Password: admin (对应的 bcrypted hash)
|
||||
if in.Email == "admin@admin.com" || in.Id == 1 {
|
||||
return &core.GetUserInfoResp{
|
||||
Id: 1,
|
||||
Email: "admin@admin.com",
|
||||
Role: "admin", // 模拟管理员
|
||||
Password: "$2a$10$X8H.V2hG1E8c3rT5fH8h3.3nK290X8t9gY1N4/n.9s3.m4G0W.3yW", // `admin` 加密后的结果
|
||||
Uuid: "mock-uuid-123",
|
||||
IsDisabled: false,
|
||||
IsDeleted: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 模拟未找到用户
|
||||
return &core.GetUserInfoResp{
|
||||
Id: 0, // Id=0 表示没找到
|
||||
}, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user