Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855beb7fa9 | ||
|
|
f57816b1b0 | ||
|
|
ff08fefc30 | ||
|
|
f792ab9055 | ||
|
|
63af103a4e | ||
|
|
0a36d44cec | ||
|
|
a1f4e1de84 | ||
|
|
05b20d47db | ||
|
|
6b746f13d1 | ||
|
|
e838d5bcd2 | ||
|
|
0dcc21260e | ||
|
|
3c30ad145c | ||
|
|
06b0a8e873 | ||
|
|
b7de2ccadd | ||
|
|
b52c5cfca1 | ||
|
|
fe910c37cf | ||
|
|
337ef330eb | ||
|
|
ffa47177aa | ||
|
|
46a76853c3 | ||
|
|
2cd7dfb2b3 | ||
|
|
fee2808bca | ||
|
|
49e5eb186a | ||
|
|
dee2865466 | ||
|
|
eb340b2615 | ||
|
|
e714549a95 | ||
|
|
a1367bcd3d | ||
|
|
642351dafd |
26
.dockerignore
Normal file
26
.dockerignore
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Ignore Docker Compose configuration files
|
||||||
|
docker-compose.yaml
|
||||||
|
|
||||||
|
# Ignore development Dockerfile
|
||||||
|
Dockerfile.dev
|
||||||
|
|
||||||
|
# Ignore the data directory
|
||||||
|
data/
|
||||||
|
|
||||||
|
# Ignore version control system directories
|
||||||
|
.git/
|
||||||
|
|
||||||
|
# Ignore log and temporary files
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Ignore editor/IDE configuration files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Ignore binaries and build cache
|
||||||
|
release/
|
||||||
|
bin/
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
72
Dockerfile.dev
Normal file
72
Dockerfile.dev
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Use build arguments for Go version and architecture
|
||||||
|
ARG GO_VERSION=1.22
|
||||||
|
ARG BUILDARCH=amd64
|
||||||
|
|
||||||
|
# Stage 1: Builder Stage
|
||||||
|
# FROM golang:${GO_VERSION}-alpine AS builder
|
||||||
|
FROM crazymax/xgo:${GO_VERSION} AS builder-backend
|
||||||
|
|
||||||
|
# Set up working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Step 1: Copy the source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Step 2: Download dependencies
|
||||||
|
RUN go mod tidy && go mod download
|
||||||
|
|
||||||
|
|
||||||
|
# Step 3: Install swag and Run the build script
|
||||||
|
RUN go install github.com/swaggo/swag/cmd/swag@latest && \
|
||||||
|
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin && \
|
||||||
|
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||||
|
|
||||||
|
# Build the Go application with CGO enabled and specified ldflags
|
||||||
|
RUN CGO_ENABLED=1 GOOS=linux go build -a \
|
||||||
|
-ldflags "-s -w --extldflags '-static -fpic'" \
|
||||||
|
-installsuffix cgo -o release/apimain cmd/apimain.go
|
||||||
|
|
||||||
|
# Stage 2: Frontend Build Stage (builder2)
|
||||||
|
FROM node:18-alpine AS builder-admin-frontend
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
RUN apk update && apk add git --no-cache
|
||||||
|
|
||||||
|
# Clone the frontend repository
|
||||||
|
RUN git clone https://github.com/lejianwen/rustdesk-api-web .
|
||||||
|
|
||||||
|
# Install npm dependencies and build the frontend
|
||||||
|
RUN npm install && npm run build
|
||||||
|
|
||||||
|
# Stage 2: Final Image
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Set up working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install necessary runtime dependencies
|
||||||
|
RUN apk add --no-cache tzdata file
|
||||||
|
|
||||||
|
# Copy the built application and resources from the builder stage
|
||||||
|
COPY --from=builder-backend /app/release /app/
|
||||||
|
COPY --from=builder-backend /app/conf /app/conf/
|
||||||
|
COPY --from=builder-backend /app/resources /app/resources/
|
||||||
|
COPY --from=builder-backend /app/docs /app/docs/
|
||||||
|
# Copy frontend build from builder2 stage
|
||||||
|
COPY --from=builder-admin-frontend /frontend/dist/ /app/resources/admin/
|
||||||
|
|
||||||
|
# Ensure the binary is correctly built and linked
|
||||||
|
RUN file /app/apimain && \
|
||||||
|
mkdir -p /app/data && \
|
||||||
|
mkdir -p /app/runtime
|
||||||
|
|
||||||
|
# Set up a volume for persistent data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
|
# Expose the necessary port
|
||||||
|
EXPOSE 21114
|
||||||
|
|
||||||
|
# Define the command to run the application
|
||||||
|
CMD ["./apimain"]
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
- 登录
|
- 登录
|
||||||
- 地址簿
|
- 地址簿
|
||||||
- 群组
|
- 群组
|
||||||
- 授权登录,支持`github`和`google`登录,支持`web后台`授权登录
|
- 授权登录,支持`github`, `google` 和 `OIDC` 登录,支持`web后台`授权登录
|
||||||
- i18n
|
- i18n
|
||||||
- Web Admin
|
- Web Admin
|
||||||
- 用户管理
|
- 用户管理
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
#### 登录
|
#### 登录
|
||||||
|
|
||||||
- 添加了`github`和`google`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
|
- 添加了`github`, `google` 以及`OIDC`授权登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
|
||||||
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
|
- 添加了web后台授权登录,点击后直接登录后台就自动登录客户端了
|
||||||
|
|
||||||

|

|
||||||
@@ -124,8 +124,10 @@
|
|||||||
4. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
4. 可以直接打开webclient,方便使用;也可以分享给游客,游客可以直接通过webclient远程到设备
|
||||||
|
|
||||||

|

|
||||||
5. Oauth,暂时只支持了`Github`和`Google`, 需要创建一个`OAuth App`,然后配置到后台
|
5. Oauth,支持了`Github`, `Google` 以及 `OIDC`, 需要创建一个`OAuth App`,然后配置到后台
|
||||||

|

|
||||||
|
- 对于`Google` 和 `Github`, `Issuer` 和 `Scopes`不需要填写.
|
||||||
|
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email` 和`preferred_username`
|
||||||
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
|
- `github oauth app`在`Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
|
||||||
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
|
||||||
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ desktop software that provides self-hosted solutions.
|
|||||||
- Login
|
- Login
|
||||||
- Address Book
|
- Address Book
|
||||||
- Groups
|
- Groups
|
||||||
- Authorized login, supports `GitHub` and `Google` login, supports `web admin` authorized login
|
- Authorized login, supports `GitHub`, `Google` and `OIDC` login, supports `web admin` authorized login
|
||||||
- i18n
|
- i18n
|
||||||
- Web Admin
|
- Web Admin
|
||||||
- User Management
|
- User Management
|
||||||
@@ -93,7 +93,7 @@ Basic implementation of the PC client's primary interfaces.Supports the Personal
|
|||||||
|
|
||||||
#### Login
|
#### Login
|
||||||
|
|
||||||
- Added `GitHub` and `Google` login, which can be used after configuration in the admin panel. See the OAuth
|
- Added `GitHub`, `Google` and `OIDC` login, which can be used after configuration in the admin panel. See the OAuth
|
||||||
configuration section for details.
|
configuration section for details.
|
||||||
- Added authorization login for the web admin panel.
|
- Added authorization login for the web admin panel.
|
||||||
|
|
||||||
@@ -128,9 +128,11 @@ installation are `admin` `admin`, please change the password immediately.
|
|||||||
4. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
|
4. You can directly launch the client or open the web client for convenience; you can also share it with guests, who can remotely access the device via the web client.
|
||||||
|
|
||||||

|

|
||||||
5. OAuth support: Currently, `GitHub` and `Google` is supported. You need to create an `OAuth App` and configure it in
|
5. OAuth support: Currently, `GitHub`, `Google` and `OIDC` are supported. You need to create an `OAuth App` and configure it in
|
||||||
the admin panel.
|
the admin panel.
|
||||||

|

|
||||||
|
- For `Google` and `Github`, you don't need to fill the `Issuer` and `Scpoes`
|
||||||
|
- For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username`
|
||||||
- Create a `GitHub OAuth App`
|
- Create a `GitHub OAuth App`
|
||||||
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
|
||||||
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
|
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
|
||||||
|
|||||||
38
build.sh
Normal file → Executable file
38
build.sh
Normal file → Executable file
@@ -1,16 +1,46 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
rm release -rf
|
set -e
|
||||||
|
# Automatically get the current environment's GOARCH; if not defined, use the detected system architecture
|
||||||
|
GOARCH=${GOARCH:-$(go env GOARCH)}
|
||||||
|
DOCS="true"
|
||||||
|
# Safely remove the old release directory
|
||||||
|
rm -rf release
|
||||||
|
|
||||||
|
# Set Go environment variables
|
||||||
go env -w GO111MODULE=on
|
go env -w GO111MODULE=on
|
||||||
go env -w GOPROXY=https://goproxy.cn,direct
|
go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
go env -w CGO_ENABLED=1
|
go env -w CGO_ENABLED=1
|
||||||
go env -w GOOS=linux
|
go env -w GOOS=linux
|
||||||
go env -w GOARCH=amd64
|
go env -w GOARCH=${GOARCH}
|
||||||
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
|
||||||
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
|
||||||
|
# Generate Swagger documentation if DOCS is not empty
|
||||||
|
if [ -n "${DOCS}" ]; then
|
||||||
|
# Check if swag is installed
|
||||||
|
if ! command -v swag &> /dev/null; then
|
||||||
|
echo "swag command not found. Please install it using:"
|
||||||
|
echo "go install github.com/swaggo/swag/cmd/swag@latest"
|
||||||
|
echo "Skipping Swagger documentation generation due to missing swag tool."
|
||||||
|
else
|
||||||
|
echo "Generating Swagger documentation..."
|
||||||
|
swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
|
||||||
|
swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipping Swagger documentation generation due to DOCS is empty."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile the Go code and output it to the release directory
|
||||||
go build -o release/apimain cmd/apimain.go
|
go build -o release/apimain cmd/apimain.go
|
||||||
|
|
||||||
|
# Copy resource files to the release directory
|
||||||
cp -ar resources release/
|
cp -ar resources release/
|
||||||
cp -ar docs release/
|
cp -ar docs release/
|
||||||
cp -ar conf release/
|
cp -ar conf release/
|
||||||
|
|
||||||
|
# Create necessary directory structures
|
||||||
mkdir -p release/data
|
mkdir -p release/data
|
||||||
mkdir -p release/runtime
|
mkdir -p release/runtime
|
||||||
|
|
||||||
|
echo "Build and setup completed successfully."
|
||||||
@@ -101,7 +101,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 240
|
version := 243
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
|
|||||||
@@ -11,3 +11,10 @@ type GoogleOauth struct {
|
|||||||
ClientSecret string `mapstructure:"client-secret"`
|
ClientSecret string `mapstructure:"client-secret"`
|
||||||
RedirectUrl string `mapstructure:"redirect-url"`
|
RedirectUrl string `mapstructure:"redirect-url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OidcOauth struct {
|
||||||
|
Issuer string `mapstructure:"issuer"`
|
||||||
|
ClientId string `mapstructure:"client-id"`
|
||||||
|
ClientSecret string `mapstructure:"client-secret"`
|
||||||
|
RedirectUrl string `mapstructure:"redirect-url"`
|
||||||
|
}
|
||||||
20
docker-compose-dev.yaml
Normal file
20
docker-compose-dev.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
rustdesk-api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
# image: lejianwen/rustdesk-api
|
||||||
|
container_name: rustdesk-api
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Shanghai
|
||||||
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||||
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||||
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://127.0.0.1:21114
|
||||||
|
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
||||||
|
ports:
|
||||||
|
- 21114:21114
|
||||||
|
volumes:
|
||||||
|
- ./data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
||||||
|
- ./conf:/app/conf # config
|
||||||
|
# - ./resources:/app/resources # 静态资源
|
||||||
|
restart: unless-stopped
|
||||||
@@ -6,10 +6,12 @@ services:
|
|||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
- RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
|
||||||
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
|
||||||
- RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
|
- RUSTDESK_API_RUSTDESK_API_SERVER=http://127.0.0.1:21114
|
||||||
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
- RUSTDESK_API_RUSTDESK_KEY=123456789
|
||||||
ports:
|
ports:
|
||||||
- 21114:21114
|
- 21114:21114
|
||||||
volumes:
|
volumes:
|
||||||
- /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
|
- ./data/rustdesk/api:/app/data # database
|
||||||
|
# - ./conf:/app/conf # config
|
||||||
|
# - ./resources:/app/resources # 静态资源
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -1453,6 +1453,38 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/login-options": {
|
||||||
|
"post": {
|
||||||
|
"description": "登录选项",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "登录选项",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/loginLog/delete": {
|
"/admin/loginLog/delete": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1922,6 +1954,63 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/oidc/auth": {
|
||||||
|
"post": {
|
||||||
|
"description": "OidcAuth",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Oauth"
|
||||||
|
],
|
||||||
|
"summary": "OidcAuth",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/oidc/auth-query": {
|
||||||
|
"get": {
|
||||||
|
"description": "OidcAuthQuery",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Oauth"
|
||||||
|
],
|
||||||
|
"summary": "OidcAuthQuery",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/admin.LoginPayload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/peer/create": {
|
"/admin/peer/create": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -3164,11 +3253,17 @@ const docTemplateadmin = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"issuer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3749,12 +3844,18 @@ const docTemplateadmin = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"issuer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -3795,6 +3896,9 @@ const docTemplateadmin = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"last_online_ip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"last_online_time": {
|
"last_online_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -3964,6 +4068,14 @@ const docTemplateadmin = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"response.ErrorResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"response.Response": {
|
"response.Response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -1446,6 +1446,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/login-options": {
|
||||||
|
"post": {
|
||||||
|
"description": "登录选项",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"登录"
|
||||||
|
],
|
||||||
|
"summary": "登录选项",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/loginLog/delete": {
|
"/admin/loginLog/delete": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -1915,6 +1947,63 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/admin/oidc/auth": {
|
||||||
|
"post": {
|
||||||
|
"description": "OidcAuth",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Oauth"
|
||||||
|
],
|
||||||
|
"summary": "OidcAuth",
|
||||||
|
"responses": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/admin/oidc/auth-query": {
|
||||||
|
"get": {
|
||||||
|
"description": "OidcAuthQuery",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Oauth"
|
||||||
|
],
|
||||||
|
"summary": "OidcAuthQuery",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/admin.LoginPayload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/admin/peer/create": {
|
"/admin/peer/create": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -3157,11 +3246,17 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"issuer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3742,12 +3837,18 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"issuer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"op": {
|
"op": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"redirect_url": {
|
"redirect_url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -3788,6 +3889,9 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"last_online_ip": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"last_online_time": {
|
"last_online_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -3957,6 +4061,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"response.ErrorResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"response.Response": {
|
"response.Response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -105,10 +105,14 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
issuer:
|
||||||
|
type: string
|
||||||
op:
|
op:
|
||||||
type: string
|
type: string
|
||||||
redirect_url:
|
redirect_url:
|
||||||
type: string
|
type: string
|
||||||
|
scopes:
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- client_id
|
- client_id
|
||||||
- client_secret
|
- client_secret
|
||||||
@@ -500,10 +504,14 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
issuer:
|
||||||
|
type: string
|
||||||
op:
|
op:
|
||||||
type: string
|
type: string
|
||||||
redirect_url:
|
redirect_url:
|
||||||
type: string
|
type: string
|
||||||
|
scopes:
|
||||||
|
type: string
|
||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
@@ -530,6 +538,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
last_online_ip:
|
||||||
|
type: string
|
||||||
last_online_time:
|
last_online_time:
|
||||||
type: integer
|
type: integer
|
||||||
memory:
|
memory:
|
||||||
@@ -643,6 +653,11 @@ definitions:
|
|||||||
total:
|
total:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
response.ErrorResponse:
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
response.Response:
|
response.Response:
|
||||||
properties:
|
properties:
|
||||||
code:
|
code:
|
||||||
@@ -1510,6 +1525,27 @@ paths:
|
|||||||
summary: 登录
|
summary: 登录
|
||||||
tags:
|
tags:
|
||||||
- 登录
|
- 登录
|
||||||
|
/admin/login-options:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 登录选项
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.ErrorResponse'
|
||||||
|
summary: 登录选项
|
||||||
|
tags:
|
||||||
|
- 登录
|
||||||
/admin/loginLog/delete:
|
/admin/loginLog/delete:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -1789,6 +1825,41 @@ paths:
|
|||||||
summary: Oauth编辑
|
summary: Oauth编辑
|
||||||
tags:
|
tags:
|
||||||
- Oauth
|
- Oauth
|
||||||
|
/admin/oidc/auth:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: OidcAuth
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses: {}
|
||||||
|
summary: OidcAuth
|
||||||
|
tags:
|
||||||
|
- Oauth
|
||||||
|
/admin/oidc/auth-query:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: OidcAuthQuery
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.Response'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/admin.LoginPayload'
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: OidcAuthQuery
|
||||||
|
tags:
|
||||||
|
- Oauth
|
||||||
/admin/peer/create:
|
/admin/peer/create:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -834,7 +834,7 @@ const docTemplateapi = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/login-options": {
|
"/login-options": {
|
||||||
"post": {
|
"get": {
|
||||||
"description": "登录选项",
|
"description": "登录选项",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
|||||||
@@ -827,7 +827,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/login-options": {
|
"/login-options": {
|
||||||
"post": {
|
"get": {
|
||||||
"description": "登录选项",
|
"description": "登录选项",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
|||||||
@@ -715,7 +715,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- 登录
|
- 登录
|
||||||
/login-options:
|
/login-options:
|
||||||
post:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 登录选项
|
description: 登录选项
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 21 KiB |
@@ -234,7 +234,7 @@ func (ct *AddressBook) Update(c *gin.Context) {
|
|||||||
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := service.AllService.AddressBookService.Update(t)
|
err := service.AllService.AddressBookService.UpdateAll(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
|
"Gwen/http/controller/api"
|
||||||
"Gwen/http/request/admin"
|
"Gwen/http/request/admin"
|
||||||
|
apiReq "Gwen/http/request/api"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
adResp "Gwen/http/response/admin"
|
adResp "Gwen/http/response/admin"
|
||||||
"Gwen/model"
|
"Gwen/model"
|
||||||
"Gwen/service"
|
"Gwen/service"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Login struct {
|
type Login struct {
|
||||||
@@ -51,7 +54,7 @@ func (ct *Login) Login(c *gin.Context) {
|
|||||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
UserId: u.Id,
|
UserId: u.Id,
|
||||||
Client: "webadmin",
|
Client: "webadmin",
|
||||||
Uuid: "",
|
Uuid: "", //must be empty
|
||||||
Ip: c.ClientIP(),
|
Ip: c.ClientIP(),
|
||||||
Type: "account",
|
Type: "account",
|
||||||
Platform: f.Platform,
|
Platform: f.Platform,
|
||||||
@@ -82,3 +85,87 @@ func (ct *Login) Logout(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoginOptions
|
||||||
|
// @Tags 登录
|
||||||
|
// @Summary 登录选项
|
||||||
|
// @Description 登录选项
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} []string
|
||||||
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
|
// @Router /admin/login-options [post]
|
||||||
|
func (ct *Login) LoginOptions(c *gin.Context) {
|
||||||
|
res := service.AllService.OauthService.List(1, 100, func(tx *gorm.DB) {
|
||||||
|
tx.Select("op").Order("id")
|
||||||
|
})
|
||||||
|
var ops []string
|
||||||
|
for _, v := range res.Oauths {
|
||||||
|
ops = append(ops, v.Op)
|
||||||
|
}
|
||||||
|
response.Success(c, ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OidcAuth
|
||||||
|
// @Tags Oauth
|
||||||
|
// @Summary OidcAuth
|
||||||
|
// @Description OidcAuth
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Router /admin/oidc/auth [post]
|
||||||
|
func (ct *Login) OidcAuth(c *gin.Context) {
|
||||||
|
// o := &api.Oauth{}
|
||||||
|
// o.OidcAuth(c)
|
||||||
|
f := &apiReq.OidcAuthRequest{}
|
||||||
|
err := c.ShouldBindJSON(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
|
||||||
|
if err != nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
|
||||||
|
Action: service.OauthActionTypeLogin,
|
||||||
|
Op: f.Op,
|
||||||
|
Id: f.Id,
|
||||||
|
DeviceType: "webadmin",
|
||||||
|
// DeviceOs: ct.Platform(c),
|
||||||
|
DeviceOs: f.DeviceInfo.Os,
|
||||||
|
Uuid: f.Uuid,
|
||||||
|
}, 5*60)
|
||||||
|
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"code": code,
|
||||||
|
"url": url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OidcAuthQuery
|
||||||
|
// @Tags Oauth
|
||||||
|
// @Summary OidcAuthQuery
|
||||||
|
// @Description OidcAuthQuery
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response{data=adResp.LoginPayload}
|
||||||
|
// @Failure 500 {object} response.Response
|
||||||
|
// @Router /admin/oidc/auth-query [get]
|
||||||
|
func (ct *Login) OidcAuthQuery(c *gin.Context) {
|
||||||
|
o := &api.Oauth{}
|
||||||
|
u, ut := o.OidcAuthQueryPre(c)
|
||||||
|
if ut == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//fmt.Println("u:", u)
|
||||||
|
//fmt.Println("ut:", ut)
|
||||||
|
response.Success(c, &adResp.LoginPayload{
|
||||||
|
Token: ut.Token,
|
||||||
|
Username: u.Username,
|
||||||
|
RouteNames: service.AllService.UserService.RouteNames(u),
|
||||||
|
Nickname: u.Nickname,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ func (o *Oauth) BindConfirm(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := service.AllService.UserService.CurUser(c)
|
u := service.AllService.UserService.CurUser(c)
|
||||||
err = service.AllService.OauthService.BindGithubUser(v.ThirdOpenId, v.ThirdOpenId, u.Id)
|
err = service.AllService.OauthService.BindOauthUser(v.Op, v.ThirdOpenId, v.ThirdName, u.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Fail(c, 101, response.TranslateMsg(c, "BindFail"))
|
response.Fail(c, 101, response.TranslateMsg(c, "BindFail"))
|
||||||
return
|
return
|
||||||
@@ -140,6 +140,13 @@ func (o *Oauth) Unbind(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if f.Op == model.OauthTypeOidc {
|
||||||
|
err = service.AllService.OauthService.UnBindOidcUser(u.Id)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,3 +207,21 @@ func (ct *Peer) BatchDelete(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.Success(c, nil)
|
response.Success(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ct *Peer) SimpleData(c *gin.Context) {
|
||||||
|
f := &admin.SimpleDataQuery{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(f.Ids) == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := service.AllService.PeerService.List(1, 99999, func(tx *gorm.DB) {
|
||||||
|
//可以公开的情报
|
||||||
|
tx.Select("id,version")
|
||||||
|
tx.Where("id in (?)", f.Ids)
|
||||||
|
})
|
||||||
|
response.Success(c, res)
|
||||||
|
}
|
||||||
|
|||||||
@@ -689,9 +689,9 @@ func (a *Ab) PeerDel(c *gin.Context) {
|
|||||||
// @Router /ab/peer/update/{guid} [put]
|
// @Router /ab/peer/update/{guid} [put]
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
func (a *Ab) PeerUpdate(c *gin.Context) {
|
func (a *Ab) PeerUpdate(c *gin.Context) {
|
||||||
//f := &gin.H{}
|
f := gin.H{}
|
||||||
f := &requstform.PersonalAddressBookForm{}
|
//f := &requstform.PersonalAddressBookForm{}
|
||||||
err := c.ShouldBindJSON(f)
|
err := c.ShouldBindJSON(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
@@ -709,17 +709,33 @@ func (a *Ab) PeerUpdate(c *gin.Context) {
|
|||||||
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
response.Error(c, response.TranslateMsg(c, "NoAccess"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println(f)
|
//fmt.Println(f)
|
||||||
//return
|
//判断f["Id"]是否存在
|
||||||
ab := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(uid, f.Id, cid)
|
fid, ok := f["id"]
|
||||||
|
if !ok {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fidstr := fid.(string)
|
||||||
|
|
||||||
|
ab := service.AllService.AddressBookService.InfoByUserIdAndIdAndCid(uid, fidstr, cid)
|
||||||
if ab == nil || ab.RowId == 0 {
|
if ab == nil || ab.RowId == 0 {
|
||||||
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
response.Error(c, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nab := f.ToAddressBook()
|
//允许的字段
|
||||||
nab.RowId = ab.RowId
|
allowUp := []string{"password", "hash", "tags", "alias"}
|
||||||
err = service.AllService.AddressBookService.Update(nab)
|
//f中的字段如果不在allowUp中,就删除
|
||||||
|
for k := range f {
|
||||||
|
if !utils.InArray(k, allowUp) {
|
||||||
|
delete(f, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fmt.Println(f)
|
||||||
|
if tags, _ok := f["tags"]; _ok {
|
||||||
|
f["tags"], _ = json.Marshal(tags)
|
||||||
|
}
|
||||||
|
err = service.AllService.AddressBookService.UpdateByMap(ab, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -54,10 +54,9 @@ func (i *Index) Heartbeat(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//如果在一分钟以内则不更新
|
//如果在40s以内则不更新
|
||||||
if time.Now().Unix()-peer.LastOnlineTime > 60 {
|
if time.Now().Unix()-peer.LastOnlineTime > 40 {
|
||||||
peer.LastOnlineTime = time.Now().Unix()
|
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
|
||||||
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: peer.LastOnlineTime}
|
|
||||||
service.AllService.PeerService.Update(upp)
|
service.AllService.PeerService.Update(upp)
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (l *Login) Login(c *gin.Context) {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} []string
|
// @Success 200 {object} []string
|
||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /login-options [post]
|
// @Router /login-options [get]
|
||||||
func (l *Login) LoginOptions(c *gin.Context) {
|
func (l *Login) LoginOptions(c *gin.Context) {
|
||||||
oauthOks := []string{}
|
oauthOks := []string{}
|
||||||
err, _ := service.AllService.OauthService.GetOauthConfig(model.OauthTypeGithub)
|
err, _ := service.AllService.OauthService.GetOauthConfig(model.OauthTypeGithub)
|
||||||
@@ -92,6 +92,10 @@ func (l *Login) LoginOptions(c *gin.Context) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
oauthOks = append(oauthOks, model.OauthTypeGoogle)
|
oauthOks = append(oauthOks, model.OauthTypeGoogle)
|
||||||
}
|
}
|
||||||
|
err, _ = service.AllService.OauthService.GetOauthConfig(model.OauthTypeOidc)
|
||||||
|
if err == nil {
|
||||||
|
oauthOks = append(oauthOks, model.OauthTypeOidc)
|
||||||
|
}
|
||||||
oauthOks = append(oauthOks, model.OauthTypeWebauth)
|
oauthOks = append(oauthOks, model.OauthTypeWebauth)
|
||||||
var oidcItems []map[string]string
|
var oidcItems []map[string]string
|
||||||
for _, v := range oauthOks {
|
for _, v := range oauthOks {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.Op != model.OauthTypeWebauth && f.Op != model.OauthTypeGoogle && f.Op != model.OauthTypeGithub {
|
//fmt.Println(f)
|
||||||
|
if f.Op != model.OauthTypeWebauth && f.Op != model.OauthTypeGoogle && f.Op != model.OauthTypeGithub && f.Op != model.OauthTypeOidc {
|
||||||
response.Error(c, response.TranslateMsg(c, "ParamsError"))
|
response.Error(c, response.TranslateMsg(c, "ParamsError"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,59 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Oauth) OidcAuthQueryPre(c *gin.Context) (*model.User, *model.UserToken) {
|
||||||
|
var u *model.User
|
||||||
|
var ut *model.UserToken
|
||||||
|
q := &api.OidcAuthQuery{}
|
||||||
|
|
||||||
|
// 解析查询参数并处理错误
|
||||||
|
if err := c.ShouldBindQuery(q); err != nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "ParamsError")+": "+err.Error())
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 OAuth 缓存
|
||||||
|
v := service.AllService.OauthService.GetOauthCache(q.Code)
|
||||||
|
if v == nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "OauthExpired"))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 UserId 为 0,说明还在授权中
|
||||||
|
if v.UserId == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind"})
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
u = service.AllService.UserService.InfoById(v.UserId)
|
||||||
|
if u == nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "UserNotFound"))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 OAuth 缓存
|
||||||
|
service.AllService.OauthService.DeleteOauthCache(q.Code)
|
||||||
|
|
||||||
|
// 创建登录日志并生成用户令牌
|
||||||
|
ut = service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
|
UserId: u.Id,
|
||||||
|
Client: v.DeviceType,
|
||||||
|
Uuid: v.Uuid,
|
||||||
|
Ip: c.ClientIP(),
|
||||||
|
Type: model.LoginLogTypeOauth,
|
||||||
|
Platform: v.DeviceOs,
|
||||||
|
})
|
||||||
|
|
||||||
|
if ut == nil {
|
||||||
|
response.Error(c, response.TranslateMsg(c, "LoginFailed"))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回用户令牌
|
||||||
|
return u, ut
|
||||||
|
}
|
||||||
|
|
||||||
// OidcAuthQuery
|
// OidcAuthQuery
|
||||||
// @Tags Oauth
|
// @Tags Oauth
|
||||||
// @Summary OidcAuthQuery
|
// @Summary OidcAuthQuery
|
||||||
@@ -69,33 +123,10 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
|
|||||||
// @Failure 500 {object} response.ErrorResponse
|
// @Failure 500 {object} response.ErrorResponse
|
||||||
// @Router /oidc/auth-query [get]
|
// @Router /oidc/auth-query [get]
|
||||||
func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
func (o *Oauth) OidcAuthQuery(c *gin.Context) {
|
||||||
q := &api.OidcAuthQuery{}
|
u, ut := o.OidcAuthQueryPre(c)
|
||||||
err := c.ShouldBindQuery(q)
|
if u == nil || ut == nil {
|
||||||
if err != nil {
|
|
||||||
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := service.AllService.OauthService.GetOauthCache(q.Code)
|
|
||||||
if v == nil {
|
|
||||||
response.Error(c, response.TranslateMsg(c, "OauthExpired"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v.UserId == 0 {
|
|
||||||
//正在授权
|
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
//fmt.Println("auth success u", u)
|
|
||||||
service.AllService.OauthService.DeleteOauthCache(q.Code)
|
|
||||||
ut := service.AllService.UserService.Login(u, &model.LoginLog{
|
|
||||||
UserId: u.Id,
|
|
||||||
Client: v.DeviceType,
|
|
||||||
Uuid: v.Uuid,
|
|
||||||
Ip: c.ClientIP(),
|
|
||||||
Type: model.LoginLogTypeOauth,
|
|
||||||
Platform: v.DeviceOs,
|
|
||||||
})
|
|
||||||
c.JSON(http.StatusOK, apiResp.LoginRes{
|
c.JSON(http.StatusOK, apiResp.LoginRes{
|
||||||
AccessToken: ut.Token,
|
AccessToken: ut.Token,
|
||||||
Type: "access_token",
|
Type: "access_token",
|
||||||
@@ -129,7 +160,11 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
|
|
||||||
ty := v.Op
|
ty := v.Op
|
||||||
ac := v.Action
|
ac := v.Action
|
||||||
|
var u *model.User
|
||||||
|
openid := ""
|
||||||
|
thirdName := ""
|
||||||
//fmt.Println("ty ac ", ty, ac)
|
//fmt.Println("ty ac ", ty, ac)
|
||||||
|
|
||||||
if ty == model.OauthTypeGithub {
|
if ty == model.OauthTypeGithub {
|
||||||
code := c.Query("code")
|
code := c.Query("code")
|
||||||
err, userData := service.AllService.OauthService.GithubCallback(code)
|
err, userData := service.AllService.OauthService.GithubCallback(code)
|
||||||
@@ -137,123 +172,100 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
|
|||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ac == service.OauthActionTypeBind {
|
openid = strconv.Itoa(userData.Id)
|
||||||
//fmt.Println("bind", ty, userData)
|
thirdName = userData.Login
|
||||||
utr := service.AllService.OauthService.UserThirdInfo(ty, strconv.Itoa(userData.Id))
|
} else if ty == model.OauthTypeGoogle {
|
||||||
if utr.UserId > 0 {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
if u == nil {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定github
|
|
||||||
err = service.AllService.OauthService.BindGithubUser(strconv.Itoa(userData.Id), userData.Login, v.UserId)
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
|
||||||
return
|
|
||||||
} else if ac == service.OauthActionTypeLogin {
|
|
||||||
//登录
|
|
||||||
if v.UserId != 0 {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoByGithubId(strconv.Itoa(userData.Id))
|
|
||||||
if u == nil {
|
|
||||||
oa := service.AllService.OauthService.InfoByOp(ty)
|
|
||||||
if !*oa.AutoRegister {
|
|
||||||
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
|
||||||
v.ThirdName = userData.Login
|
|
||||||
v.ThirdOpenId = strconv.Itoa(userData.Id)
|
|
||||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
|
||||||
c.Redirect(http.StatusFound, url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//自动注册
|
|
||||||
u = service.AllService.UserService.RegisterByGithub(userData.Login, strconv.Itoa(userData.Id))
|
|
||||||
if u.Id == 0 {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.UserId = u.Id
|
|
||||||
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ty == model.OauthTypeGoogle {
|
|
||||||
code := c.Query("code")
|
code := c.Query("code")
|
||||||
err, userData := service.AllService.OauthService.GoogleCallback(code)
|
err, userData := service.AllService.OauthService.GoogleCallback(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
openid = userData.Email
|
||||||
//将空格替换成_
|
//将空格替换成_
|
||||||
googleName := strings.Replace(userData.Name, " ", "_", -1)
|
thirdName = strings.Replace(userData.Name, " ", "_", -1)
|
||||||
if ac == service.OauthActionTypeBind {
|
} else if ty == model.OauthTypeOidc {
|
||||||
//fmt.Println("bind", ty, userData)
|
code := c.Query("code")
|
||||||
utr := service.AllService.OauthService.UserThirdInfo(ty, userData.Email)
|
err, userData := service.AllService.OauthService.OidcCallback(code)
|
||||||
if utr.UserId > 0 {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
u := service.AllService.UserService.InfoById(v.UserId)
|
|
||||||
if u == nil {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//绑定
|
|
||||||
err = service.AllService.OauthService.BindGoogleUser(userData.Email, googleName, v.UserId)
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
|
||||||
return
|
|
||||||
} else if ac == service.OauthActionTypeLogin {
|
|
||||||
if v.UserId != 0 {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u := service.AllService.UserService.InfoByGoogleEmail(userData.Email)
|
|
||||||
if u == nil {
|
|
||||||
oa := service.AllService.OauthService.InfoByOp(ty)
|
|
||||||
if !*oa.AutoRegister {
|
|
||||||
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
|
||||||
|
|
||||||
v.ThirdName = googleName
|
|
||||||
v.ThirdOpenId = userData.Email
|
|
||||||
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
|
||||||
c.Redirect(http.StatusFound, url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//自动注册
|
|
||||||
u = service.AllService.UserService.RegisterByGoogle(googleName, userData.Email)
|
|
||||||
if u.Id == 0 {
|
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.UserId = u.Id
|
|
||||||
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
|
||||||
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
openid = userData.Sub
|
||||||
|
thirdName = userData.PreferredUsername
|
||||||
|
} else {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ac == service.OauthActionTypeBind {
|
||||||
|
|
||||||
|
//fmt.Println("bind", ty, userData)
|
||||||
|
utr := service.AllService.OauthService.UserThirdInfo(ty, openid)
|
||||||
|
if utr.UserId > 0 {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//绑定
|
||||||
|
u = service.AllService.UserService.InfoById(v.UserId)
|
||||||
|
if u == nil {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//绑定
|
||||||
|
err := service.AllService.OauthService.BindOauthUser(ty, openid, thirdName, v.UserId)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
|
||||||
|
return
|
||||||
|
|
||||||
|
} else if ac == service.OauthActionTypeLogin {
|
||||||
|
//登录
|
||||||
|
if v.UserId != 0 {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u = service.AllService.UserService.InfoByGithubId(openid)
|
||||||
|
if u == nil {
|
||||||
|
oa := service.AllService.OauthService.InfoByOp(ty)
|
||||||
|
if !*oa.AutoRegister {
|
||||||
|
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
|
||||||
|
v.ThirdName = thirdName
|
||||||
|
v.ThirdOpenId = openid
|
||||||
|
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
|
||||||
|
c.Redirect(http.StatusFound, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//自动注册
|
||||||
|
u = service.AllService.UserService.RegisterByOauth(ty, thirdName, openid)
|
||||||
|
if u.Id == 0 {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthRegisterFailed"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.UserId = u.Id
|
||||||
|
service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
|
||||||
|
// 如果是webadmin,登录成功后跳转到webadmin
|
||||||
|
if v.DeviceType == "webadmin" {
|
||||||
|
/*service.AllService.UserService.Login(u, &model.LoginLog{
|
||||||
|
UserId: u.Id,
|
||||||
|
Client: "webadmin",
|
||||||
|
Uuid: "", //must be empty
|
||||||
|
Ip: c.ClientIP(),
|
||||||
|
Type: model.LoginLogTypeOauth,
|
||||||
|
Platform: v.DeviceOs,
|
||||||
|
})*/
|
||||||
|
url := global.Config.Rustdesk.ApiServer + "/_admin/#/"
|
||||||
|
c.Redirect(http.StatusFound, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "SystemError"))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ type UnBindOauthForm struct {
|
|||||||
type OauthForm struct {
|
type OauthForm struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
Op string `json:"op" validate:"required"`
|
Op string `json:"op" validate:"required"`
|
||||||
|
Issuer string `json:"issuer" validate:"omitempty,url"`
|
||||||
|
Scopes string `json:"scopes" validate:"omitempty"`
|
||||||
ClientId string `json:"client_id" validate:"required"`
|
ClientId string `json:"client_id" validate:"required"`
|
||||||
ClientSecret string `json:"client_secret" validate:"required"`
|
ClientSecret string `json:"client_secret" validate:"required"`
|
||||||
RedirectUrl string `json:"redirect_url" validate:"required"`
|
RedirectUrl string `json:"redirect_url" validate:"required"`
|
||||||
@@ -28,6 +30,8 @@ func (of *OauthForm) ToOauth() *model.Oauth {
|
|||||||
ClientSecret: of.ClientSecret,
|
ClientSecret: of.ClientSecret,
|
||||||
RedirectUrl: of.RedirectUrl,
|
RedirectUrl: of.RedirectUrl,
|
||||||
AutoRegister: of.AutoRegister,
|
AutoRegister: of.AutoRegister,
|
||||||
|
Issuer: of.Issuer,
|
||||||
|
Scopes: of.Scopes,
|
||||||
}
|
}
|
||||||
oa.Id = of.Id
|
oa.Id = of.Id
|
||||||
return oa
|
return oa
|
||||||
|
|||||||
@@ -39,3 +39,7 @@ type PeerQuery struct {
|
|||||||
Id string `json:"id" form:"id"`
|
Id string `json:"id" form:"id"`
|
||||||
Hostname string `json:"hostname" form:"hostname"`
|
Hostname string `json:"hostname" form:"hostname"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SimpleDataQuery struct {
|
||||||
|
Ids []string `json:"ids" form:"ids"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ func Init(g *gin.Engine) {
|
|||||||
rs := &admin.Rustdesk{}
|
rs := &admin.Rustdesk{}
|
||||||
adg.GET("/server-config", rs.ServerConfig)
|
adg.GET("/server-config", rs.ServerConfig)
|
||||||
adg.GET("/app-config", rs.AppConfig)
|
adg.GET("/app-config", rs.AppConfig)
|
||||||
|
|
||||||
//访问静态文件
|
//访问静态文件
|
||||||
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
||||||
}
|
}
|
||||||
@@ -41,6 +40,9 @@ func LoginBind(rg *gin.RouterGroup) {
|
|||||||
cont := &admin.Login{}
|
cont := &admin.Login{}
|
||||||
rg.POST("/login", cont.Login)
|
rg.POST("/login", cont.Login)
|
||||||
rg.POST("/logout", cont.Logout)
|
rg.POST("/logout", cont.Logout)
|
||||||
|
rg.GET("/login-options", cont.LoginOptions)
|
||||||
|
rg.POST("/oidc/auth", cont.OidcAuth)
|
||||||
|
rg.GET("/oidc/auth-query", cont.OidcAuthQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserBind(rg *gin.RouterGroup) {
|
func UserBind(rg *gin.RouterGroup) {
|
||||||
@@ -112,6 +114,7 @@ func PeerBind(rg *gin.RouterGroup) {
|
|||||||
aR.POST("/create", cont.Create)
|
aR.POST("/create", cont.Create)
|
||||||
aR.POST("/update", cont.Update)
|
aR.POST("/update", cont.Update)
|
||||||
aR.POST("/delete", cont.Delete)
|
aR.POST("/delete", cont.Delete)
|
||||||
|
aR.POST("/simpleData", cont.SimpleData)
|
||||||
|
|
||||||
arp := aR.Use(middleware.AdminPrivilege())
|
arp := aR.Use(middleware.AdminPrivilege())
|
||||||
arp.POST("/batchDelete", cont.BatchDelete)
|
arp.POST("/batchDelete", cont.BatchDelete)
|
||||||
|
|||||||
@@ -51,30 +51,22 @@ func TestLocal_GetLock(t *testing.T) {
|
|||||||
func TestLocal_Lock(t *testing.T) {
|
func TestLocal_Lock(t *testing.T) {
|
||||||
l := NewLocal()
|
l := NewLocal()
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(3)
|
m := 10
|
||||||
|
wg.Add(m)
|
||||||
i := 0
|
i := 0
|
||||||
go func() {
|
for j := 0; j < m; j++ {
|
||||||
l.Lock("key")
|
go func() {
|
||||||
fmt.Println("l1", i)
|
l.Lock("key")
|
||||||
i++
|
//fmt.Println(j, i)
|
||||||
l.UnLock("key")
|
i++
|
||||||
wg.Done()
|
fmt.Println(j, i)
|
||||||
}()
|
l.UnLock("key")
|
||||||
go func() {
|
wg.Done()
|
||||||
l.Lock("key")
|
}()
|
||||||
fmt.Println("l2", i)
|
}
|
||||||
i++
|
|
||||||
l.UnLock("key")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
l.Lock("key")
|
|
||||||
fmt.Println("l3", i)
|
|
||||||
i++
|
|
||||||
l.UnLock("key")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
fmt.Println(i)
|
||||||
|
|
||||||
}
|
}
|
||||||
func TestSyncMap(t *testing.T) {
|
func TestSyncMap(t *testing.T) {
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ type Oauth struct {
|
|||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
RedirectUrl string `json:"redirect_url"`
|
RedirectUrl string `json:"redirect_url"`
|
||||||
AutoRegister *bool `json:"auto_register"`
|
AutoRegister *bool `json:"auto_register"`
|
||||||
|
Scopes string `json:"scopes"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OauthTypeGithub = "github"
|
OauthTypeGithub = "github"
|
||||||
OauthTypeGoogle = "google"
|
OauthTypeGoogle = "google"
|
||||||
|
OauthTypeOidc = "oidc"
|
||||||
OauthTypeWebauth = "webauth"
|
OauthTypeWebauth = "webauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ type Peer struct {
|
|||||||
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
|
Uuid string `json:"uuid" gorm:"default:'';not null;index"`
|
||||||
Version string `json:"version" gorm:"default:'';not null;"`
|
Version string `json:"version" gorm:"default:'';not null;"`
|
||||||
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
|
||||||
User User `json:"user,omitempty" gorm:""`
|
User *User `json:"user,omitempty"`
|
||||||
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
|
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
|
||||||
|
LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"`
|
||||||
TimeModel
|
TimeModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package model
|
|||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
IdModel
|
IdModel
|
||||||
Username string `json:"username" gorm:"default:'';not null;index,unique"`
|
Username string `json:"username" gorm:"default:'';not null;uniqueIndex"`
|
||||||
Password string `json:"-" gorm:"default:'';not null;"`
|
Password string `json:"-" gorm:"default:'';not null;"`
|
||||||
Nickname string `json:"nickname" gorm:"default:'';not null;"`
|
Nickname string `json:"nickname" gorm:"default:'';not null;"`
|
||||||
Avatar string `json:"avatar" gorm:"default:'';not null;"`
|
Avatar string `json:"avatar" gorm:"default:'';not null;"`
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ func (s *AddressBookService) Delete(u *model.AddressBook) error {
|
|||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
func (s *AddressBookService) Update(u *model.AddressBook) error {
|
func (s *AddressBookService) Update(u *model.AddressBook) error {
|
||||||
|
return global.DB.Model(u).Updates(u).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateByMap 更新
|
||||||
|
func (s *AddressBookService) UpdateByMap(u *model.AddressBook, data map[string]interface{}) error {
|
||||||
|
return global.DB.Model(u).Updates(data).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAll 更新
|
||||||
|
func (s *AddressBookService) UpdateAll(u *model.AddressBook) error {
|
||||||
return global.DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
return global.DB.Model(u).Select("*").Omit("created_at").Updates(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
197
service/oauth.go
197
service/oauth.go
@@ -15,10 +15,19 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Define a struct to parse the .well-known/openid-configuration response
|
||||||
|
type OidcEndpoint struct {
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
AuthURL string `json:"authorization_endpoint"`
|
||||||
|
TokenURL string `json:"token_endpoint"`
|
||||||
|
UserInfo string `json:"userinfo_endpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
type OauthService struct {
|
type OauthService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +87,14 @@ type GoogleUserdata struct {
|
|||||||
Picture string `json:"picture"`
|
Picture string `json:"picture"`
|
||||||
VerifiedEmail bool `json:"verified_email"`
|
VerifiedEmail bool `json:"verified_email"`
|
||||||
}
|
}
|
||||||
|
type OidcUserdata struct {
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
VerifiedEmail bool `json:"email_verified"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PreferredUsername string `json:"preferred_username"`
|
||||||
|
}
|
||||||
|
|
||||||
type OauthCacheItem struct {
|
type OauthCacheItem struct {
|
||||||
UserId uint `json:"user_id"`
|
UserId uint `json:"user_id"`
|
||||||
Id string `json:"id"` //rustdesk的设备ID
|
Id string `json:"id"` //rustdesk的设备ID
|
||||||
@@ -137,38 +154,106 @@ func (os *OauthService) BeginAuth(op string) (error error, code, url string) {
|
|||||||
return err, code, ""
|
return err, code, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOauthConfig 获取配置
|
// Method to fetch OIDC configuration dynamically
|
||||||
|
func FetchOidcConfig(issuer string) (error, OidcEndpoint) {
|
||||||
|
configURL := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration"
|
||||||
|
|
||||||
|
// Get the HTTP client (with or without proxy based on configuration)
|
||||||
|
client := getHTTPClientWithProxy()
|
||||||
|
|
||||||
|
resp, err := client.Get(configURL)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to fetch OIDC configuration"), OidcEndpoint{}
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New("OIDC configuration not found, status code: %d"), OidcEndpoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var endpoint OidcEndpoint
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&endpoint); err != nil {
|
||||||
|
return errors.New("failed to parse OIDC configuration"), OidcEndpoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOauthConfig retrieves the OAuth2 configuration based on the provider type
|
||||||
func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
|
func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
|
||||||
if op == model.OauthTypeGithub {
|
switch op {
|
||||||
g := os.InfoByOp(model.OauthTypeGithub)
|
case model.OauthTypeGithub:
|
||||||
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
|
return os.getGithubConfig()
|
||||||
return errors.New("ConfigNotFound"), nil
|
case model.OauthTypeGoogle:
|
||||||
}
|
return os.getGoogleConfig()
|
||||||
return nil, &oauth2.Config{
|
case model.OauthTypeOidc:
|
||||||
ClientID: g.ClientId,
|
return os.getOidcConfig()
|
||||||
ClientSecret: g.ClientSecret,
|
default:
|
||||||
RedirectURL: g.RedirectUrl,
|
return errors.New("unsupported OAuth type"), nil
|
||||||
Endpoint: github.Endpoint,
|
|
||||||
Scopes: []string{"read:user", "user:email"},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if op == model.OauthTypeGoogle {
|
}
|
||||||
g := os.InfoByOp(model.OauthTypeGoogle)
|
|
||||||
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
|
// Helper function to get GitHub OAuth2 configuration
|
||||||
return errors.New("ConfigNotFound"), nil
|
func (os *OauthService) getGithubConfig() (error, *oauth2.Config) {
|
||||||
}
|
g := os.InfoByOp(model.OauthTypeGithub)
|
||||||
return nil, &oauth2.Config{
|
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
|
||||||
ClientID: g.ClientId,
|
return errors.New("ConfigNotFound"), nil
|
||||||
ClientSecret: g.ClientSecret,
|
}
|
||||||
RedirectURL: g.RedirectUrl,
|
return nil, &oauth2.Config{
|
||||||
Endpoint: google.Endpoint,
|
ClientID: g.ClientId,
|
||||||
Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"},
|
ClientSecret: g.ClientSecret,
|
||||||
}
|
RedirectURL: g.RedirectUrl,
|
||||||
|
Endpoint: github.Endpoint,
|
||||||
|
Scopes: []string{"read:user", "user:email"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get Google OAuth2 configuration
|
||||||
|
func (os *OauthService) getGoogleConfig() (error, *oauth2.Config) {
|
||||||
|
g := os.InfoByOp(model.OauthTypeGoogle)
|
||||||
|
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
|
||||||
|
return errors.New("ConfigNotFound"), nil
|
||||||
|
}
|
||||||
|
return nil, &oauth2.Config{
|
||||||
|
ClientID: g.ClientId,
|
||||||
|
ClientSecret: g.ClientSecret,
|
||||||
|
RedirectURL: g.RedirectUrl,
|
||||||
|
Endpoint: google.Endpoint,
|
||||||
|
Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get OIDC OAuth2 configuration
|
||||||
|
func (os *OauthService) getOidcConfig() (error, *oauth2.Config) {
|
||||||
|
g := os.InfoByOp(model.OauthTypeOidc)
|
||||||
|
if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" || g.Issuer == "" {
|
||||||
|
return errors.New("ConfigNotFound"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set scopes
|
||||||
|
scopes := strings.TrimSpace(g.Scopes)
|
||||||
|
if scopes == "" {
|
||||||
|
scopes = "openid,profile,email"
|
||||||
|
}
|
||||||
|
scopeList := strings.Split(scopes, ",")
|
||||||
|
err, endpoint := FetchOidcConfig(g.Issuer)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
return nil, &oauth2.Config{
|
||||||
|
ClientID: g.ClientId,
|
||||||
|
ClientSecret: g.ClientSecret,
|
||||||
|
RedirectURL: g.RedirectUrl,
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: endpoint.AuthURL,
|
||||||
|
TokenURL: endpoint.TokenURL,
|
||||||
|
},
|
||||||
|
Scopes: scopeList,
|
||||||
}
|
}
|
||||||
return errors.New("ConfigNotFound"), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPClientWithProxy() *http.Client {
|
func getHTTPClientWithProxy() *http.Client {
|
||||||
|
//todo add timeout
|
||||||
if global.Config.Proxy.Enable {
|
if global.Config.Proxy.Enable {
|
||||||
if global.Config.Proxy.Host == "" {
|
if global.Config.Proxy.Host == "" {
|
||||||
global.Logger.Warn("Proxy is enabled but proxy host is empty.")
|
global.Logger.Warn("Proxy is enabled but proxy host is empty.")
|
||||||
@@ -269,6 +354,53 @@ func (os *OauthService) GoogleCallback(code string) (error error, userData *Goog
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (os *OauthService) OidcCallback(code string) (error error, userData *OidcUserdata) {
|
||||||
|
err, oauthConfig := os.GetOauthConfig(model.OauthTypeOidc)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
// 使用代理配置创建 HTTP 客户端
|
||||||
|
httpClient := getHTTPClientWithProxy()
|
||||||
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
||||||
|
|
||||||
|
token, err := oauthConfig.Exchange(ctx, code)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("oauthConfig.Exchange() failed: ", err)
|
||||||
|
error = errors.New("GetOauthTokenError")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用带有代理的 HTTP 客户端获取用户信息
|
||||||
|
client := oauthConfig.Client(ctx, token)
|
||||||
|
g := os.InfoByOp(model.OauthTypeOidc)
|
||||||
|
err, endpoint := FetchOidcConfig(g.Issuer)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("failed fetching OIDC configuration: ", err)
|
||||||
|
error = errors.New("FetchOidcConfigError")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := client.Get(endpoint.UserInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("failed getting user info: ", err)
|
||||||
|
error = errors.New("GetOauthUserInfoError")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
err := Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Warn("failed closing response body: ", err)
|
||||||
|
}
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
|
// 解析用户信息
|
||||||
|
if err = json.NewDecoder(resp.Body).Decode(&userData); err != nil {
|
||||||
|
global.Logger.Warn("failed decoding user info: ", err)
|
||||||
|
error = errors.New("DecodeOauthUserInfoError")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird {
|
func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird {
|
||||||
ut := &model.UserThird{}
|
ut := &model.UserThird{}
|
||||||
global.DB.Where("open_id = ? and third_type = ?", openid, op).First(ut)
|
global.DB.Where("open_id = ? and third_type = ?", openid, op).First(ut)
|
||||||
@@ -282,6 +414,11 @@ func (os *OauthService) BindGithubUser(openid, username string, userId uint) err
|
|||||||
func (os *OauthService) BindGoogleUser(email, username string, userId uint) error {
|
func (os *OauthService) BindGoogleUser(email, username string, userId uint) error {
|
||||||
return os.BindOauthUser(model.OauthTypeGoogle, email, username, userId)
|
return os.BindOauthUser(model.OauthTypeGoogle, email, username, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (os *OauthService) BindOidcUser(sub, username string, userId uint) error {
|
||||||
|
return os.BindOauthUser(model.OauthTypeOidc, sub, username, userId)
|
||||||
|
}
|
||||||
|
|
||||||
func (os *OauthService) BindOauthUser(thirdType, openid, username string, userId uint) error {
|
func (os *OauthService) BindOauthUser(thirdType, openid, username string, userId uint) error {
|
||||||
utr := &model.UserThird{
|
utr := &model.UserThird{
|
||||||
OpenId: openid,
|
OpenId: openid,
|
||||||
@@ -298,10 +435,18 @@ func (os *OauthService) UnBindGithubUser(userid uint) error {
|
|||||||
func (os *OauthService) UnBindGoogleUser(userid uint) error {
|
func (os *OauthService) UnBindGoogleUser(userid uint) error {
|
||||||
return os.UnBindThird(model.OauthTypeGoogle, userid)
|
return os.UnBindThird(model.OauthTypeGoogle, userid)
|
||||||
}
|
}
|
||||||
|
func (os *OauthService) UnBindOidcUser(userid uint) error {
|
||||||
|
return os.UnBindThird(model.OauthTypeOidc, userid)
|
||||||
|
}
|
||||||
func (os *OauthService) UnBindThird(thirdType string, userid uint) error {
|
func (os *OauthService) UnBindThird(thirdType string, userid uint) error {
|
||||||
return global.DB.Where("user_id = ? and third_type = ?", userid, thirdType).Delete(&model.UserThird{}).Error
|
return global.DB.Where("user_id = ? and third_type = ?", userid, thirdType).Delete(&model.UserThird{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUserByUserId: When user is deleted, delete all third party bindings
|
||||||
|
func (os *OauthService) DeleteUserByUserId(userid uint) error {
|
||||||
|
return global.DB.Where("user_id = ?", userid).Delete(&model.UserThird{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
// InfoById 根据id取用户信息
|
// InfoById 根据id取用户信息
|
||||||
func (os *OauthService) InfoById(id uint) *model.Oauth {
|
func (os *OauthService) InfoById(id uint) *model.Oauth {
|
||||||
u := &model.Oauth{}
|
u := &model.Oauth{}
|
||||||
|
|||||||
@@ -148,8 +148,37 @@ func (us *UserService) Create(u *model.User) error {
|
|||||||
func (us *UserService) Logout(u *model.User, token string) error {
|
func (us *UserService) Logout(u *model.User, token string) error {
|
||||||
return global.DB.Where("user_id = ? and token = ?", u.Id, token).Delete(&model.UserToken{}).Error
|
return global.DB.Where("user_id = ? and token = ?", u.Id, token).Delete(&model.UserToken{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete 删除用户和oauth信息
|
||||||
func (us *UserService) Delete(u *model.User) error {
|
func (us *UserService) Delete(u *model.User) error {
|
||||||
return global.DB.Delete(u).Error
|
tx := global.DB.Begin()
|
||||||
|
// 删除用户
|
||||||
|
if err := tx.Delete(u).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除关联的 OAuth 信息
|
||||||
|
if err := tx.Where("user_id = ?", u.Id).Delete(&model.UserThird{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除关联的ab
|
||||||
|
if err := tx.Where("user_id = ?", u.Id).Delete(&model.AddressBook{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除关联的abc
|
||||||
|
if err := tx.Where("user_id = ?", u.Id).Delete(&model.AddressBookCollection{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 删除关联的abcr
|
||||||
|
if err := tx.Where("user_id = ?", u.Id).Delete(&model.AddressBookCollectionRule{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新
|
// Update 更新
|
||||||
@@ -196,6 +225,11 @@ func (us *UserService) InfoByGoogleEmail(email string) *model.User {
|
|||||||
return us.InfoByOauthId(model.OauthTypeGithub, email)
|
return us.InfoByOauthId(model.OauthTypeGithub, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InfoByOidcSub 根据oidc取用户信息
|
||||||
|
func (us *UserService) InfoByOidcSub(sub string) *model.User {
|
||||||
|
return us.InfoByOauthId(model.OauthTypeOidc, sub)
|
||||||
|
}
|
||||||
|
|
||||||
// InfoByOauthId 根据oauth取用户信息
|
// InfoByOauthId 根据oauth取用户信息
|
||||||
func (us *UserService) InfoByOauthId(thirdType, uid string) *model.User {
|
func (us *UserService) InfoByOauthId(thirdType, uid string) *model.User {
|
||||||
ut := AllService.OauthService.UserThirdInfo(thirdType, uid)
|
ut := AllService.OauthService.UserThirdInfo(thirdType, uid)
|
||||||
@@ -219,32 +253,42 @@ func (us *UserService) RegisterByGoogle(name string, email string) *model.User {
|
|||||||
return us.RegisterByOauth(model.OauthTypeGoogle, name, email)
|
return us.RegisterByOauth(model.OauthTypeGoogle, name, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterByOidc 注册, use PreferredUsername as username, sub as openid
|
||||||
|
func (us *UserService) RegisterByOidc(PreferredUsername string, sub string) *model.User {
|
||||||
|
return us.RegisterByOauth(model.OauthTypeOidc, PreferredUsername, sub)
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterByOauth 注册
|
// RegisterByOauth 注册
|
||||||
func (us *UserService) RegisterByOauth(thirdType, thirdName, uid string) *model.User {
|
func (us *UserService) RegisterByOauth(thirdType, thirdName, uid string) *model.User {
|
||||||
|
global.Lock.Lock("registerByOauth")
|
||||||
|
defer global.Lock.UnLock("registerByOauth")
|
||||||
|
ut := AllService.OauthService.UserThirdInfo(thirdType, uid)
|
||||||
|
if ut.Id != 0 {
|
||||||
|
u := &model.User{}
|
||||||
|
global.DB.Where("id = ?", ut.UserId).First(u)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
tx := global.DB.Begin()
|
tx := global.DB.Begin()
|
||||||
ut := &model.UserThird{
|
ut = &model.UserThird{
|
||||||
OpenId: uid,
|
OpenId: uid,
|
||||||
ThirdName: thirdName,
|
ThirdName: thirdName,
|
||||||
ThirdType: thirdType,
|
ThirdType: thirdType,
|
||||||
}
|
}
|
||||||
//global.DB.Where("open_id = ?", githubId).First(ut)
|
|
||||||
//这种情况不应该出现,如果出现说明有bug
|
|
||||||
//if ut.Id != 0 {
|
|
||||||
// u := &model.User{}
|
|
||||||
// global.DB.Where("id = ?", ut.UserId).First(u)
|
|
||||||
// tx.Commit()
|
|
||||||
// return u
|
|
||||||
//}
|
|
||||||
|
|
||||||
username := us.GenerateUsernameByOauth(thirdName)
|
username := us.GenerateUsernameByOauth(thirdName)
|
||||||
u := &model.User{
|
u := &model.User{
|
||||||
Username: username,
|
Username: username,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
}
|
}
|
||||||
global.DB.Create(u)
|
tx.Create(u)
|
||||||
|
if u.Id == 0 {
|
||||||
|
tx.Rollback()
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
ut.UserId = u.Id
|
ut.UserId = u.Id
|
||||||
global.DB.Create(ut)
|
tx.Create(ut)
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
return u
|
return u
|
||||||
|
|||||||
@@ -91,3 +91,12 @@ func Values[K comparable, V any](m map[K]V) []V {
|
|||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InArray(k string, arr []string) bool {
|
||||||
|
for _, v := range arr {
|
||||||
|
if k == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user