ljw 1 год назад
Родитель
Сommit
a4b413dadb
48 измененных файлов с 3481 добавлено и 188 удалено
  1. 78 26
      README.md
  2. 4 1
      cmd/apimain.go
  3. 5 4
      conf/config.yaml
  4. 1 0
      config/gin.go
  5. 13 0
      config/oauth.go
  6. 638 2
      docs/admin/admin_docs.go
  7. 638 2
      docs/admin/admin_swagger.json
  8. 386 0
      docs/admin/admin_swagger.yaml
  9. BIN
      docs/admin_webclient.png
  10. 149 34
      docs/api/api_docs.go
  11. 149 34
      docs/api/api_swagger.json
  12. 98 21
      docs/api/api_swagger.yaml
  13. BIN
      docs/pc_login.png
  14. BIN
      docs/web_admin.png
  15. BIN
      docs/web_admin_user.png
  16. BIN
      docs/web_user.png
  17. BIN
      docs/webclient_conf.png
  18. 3 0
      generate_run.go
  19. 7 5
      go.mod
  20. 9 1
      http/controller/admin/login.go
  21. 110 0
      http/controller/admin/loginLog.go
  22. 291 0
      http/controller/admin/oauth.go
  23. 34 0
      http/controller/admin/user.go
  24. 0 4
      http/controller/api/ab.go
  25. 42 6
      http/controller/api/login.go
  26. 222 0
      http/controller/api/ouath.go
  27. 5 5
      http/controller/api/user.go
  28. 1 1
      http/controller/api/webClient.go
  29. 7 1
      http/controller/web/index.go
  30. 11 0
      http/http.go
  31. 7 0
      http/middleware/rustauth.go
  32. 7 0
      http/request/admin/login.go
  33. 34 0
      http/request/admin/oauth.go
  34. 14 0
      http/request/api/oauth.go
  35. 14 2
      http/request/api/user.go
  36. 6 1
      http/response/admin/user.go
  37. 9 7
      http/response/api/user.go
  38. 2 28
      http/response/api/webClient.go
  39. 32 0
      http/router/admin.go
  40. 11 1
      http/router/api.go
  41. 23 0
      model/loginLog.go
  42. 22 0
      model/oauth.go
  43. 12 0
      model/userThird.go
  44. 45 0
      service/loginLog.go
  45. 256 0
      service/oauth.go
  46. 2 0
      service/service.go
  47. 72 2
      service/user.go
  48. 12 0
      utils/tools.go

+ 78 - 26
README.md

@@ -19,46 +19,66 @@
19 19
 2. server端必须指定key,不能用自带的生成的key,否则可能链接不上或者超时
20 20
 
21 21
 ```bash
22
-hbbs -r <relay-server-ip[:port]> -k 123456789
23
-hbbr -k 123456789
22
+hbbs -r <relay-server-ip[:port]> -k <key>
23
+hbbr -k <key>
24
+```
25
+
26
+比如
27
+
28
+```bash
29
+hbbs -r <relay-server-ip[:port]> -k abc1234567
30
+hbbr -k abc1234567
24 31
 ```
25 32
 
26 33
 ## 功能
27 34
 
28
-### **API 服务**: 基本实现了PC端基础的接口。
35
+### API 服务: 基本实现了PC端基础的接口。
36
+
37
+#### 登录
38
+
39
+- 添加了`github`登录,需要在后台配置好就可以用了,具体可看后台OAuth配置
40
+- 添加了web后台授权登录
41
+
42
+![pc_login](docs/pc_login.png)
43
+
44
+#### 地址簿
29 45
 
30 46
 ![pc_ab](docs/pc_ab.png)
47
+
48
+#### 群组,群组分为`共享组`和`普通组`,共享组中所有人都能看到小组成员的地址,普通组只有管理员能看到所有小组成员的地址
49
+
31 50
 ![pc_gr](docs/pc_gr.png)
32 51
 
33 52
 ### **Web UI**: 使用前后端分离,提供用户友好的管理界面,主要用来管理和展示。
34 53
 
35 54
 ***前端代码在[rustdesk-api-web](https://github.com/lejianwen/rustdesk-api-web)***
36 55
 
37
-***后台访问地址是`http://<your server>:21114/_admin/`初次安装管理员为用户名密码为`admin admin`,请即时更改密码***
56
+***后台访问地址是`http://<your server>:21114/_admin/`初次安装管理员为用户名密码为`admin` `admin`,请即时更改密码***
38 57
 
39 58
 1. 管理员界面
40 59
    ![web_admin](docs/web_admin.png)
41 60
 2. 普通用户界面
42
-   ![web_user](docs/web_user.png)
43
-3. 更改密码在右上角
44
-
61
+   ![web_user](docs/web_admin_user.png)
62
+3. 右上角也可以更改密码
45 63
    ![web_resetpwd](docs/web_resetpwd.png)
46 64
 
47 65
 4. 分组可以自定义,方便管理,暂时支持两种类型: `共享组` 和 `普通组`
48
-
49 66
    ![web_admin_gr](docs/web_admin_gr.png)
67
+5. 可以直接打开webclient,方便使用
68
+   ![web_webclient](docs/admin_webclient.png)
50 69
 
51
-### **Web 客户端**:
70
+### **Web Client**:
52 71
 
53 72
 1. 如果已经登录了后台,web client将自动直接登录
54 73
 2. 如果没登录后台,点击右上角登录即可,api server已经自动配置好了
55
-3. 登录后台后,会将地址簿自动保存到web client中,方便使用
56 74
    ![webclient_conf](docs/webclient_conf.png)
75
+3. 登录后,会自动同步ID服务器和KEY
76
+4. 登录后,会将地址簿自动保存到web client中,方便使用
57 77
 
58 78
 ### **自动化文档**: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
59 79
 
60
-1. 后台文档 <youer server>/admin/swagger/index.html
61
-2. PC端文档 <youer server>/swagger/index.html
80
+1. 后台文档 `<youer server>/admin/swagger/index.html`
81
+2. PC端文档 `<youer server>/swagger/index.html`
62 82
    ![api_swag](docs/api_swag.png)
63 83
 
64 84
 ## 安装与运行
@@ -72,6 +92,7 @@ gin:
72 92
   api-addr: "0.0.0.0:21114"
73 93
   mode: "release"
74 94
   resources-path: 'resources'
95
+  trust-proxy: ""
75 96
 gorm:
76 97
   type: "sqlite"
77 98
   max-idle-conns: 10
@@ -88,36 +109,67 @@ rustdesk:
88 109
   key: "123456789"
89 110
 ```
90 111
 
91
-### 安装步骤
92
-
93
-#### docker运行
94
-
95
-1. 直接docker运行
96
-
97
-```bash
98
-docker run -d --name rustdesk-api -p 21114:21114 -v /data/rustdesk/api:/app/data lejianwen/rustdesk-api
99
-```
100
-
101
-- 环境变量,变量名前缀是RUSTDESK_API
112
+* 环境变量,变量名前缀是RUSTDESK_API,环境变量如果存在将覆盖配置文件中的配置
102 113
 
103 114
 | 变量名                                 | 说明                                   | 示例                          |
104 115
 |:------------------------------------|:-------------------------------------|-----------------------------|
116
+| -----GIN配置-----                     | ----------                           | ----------                  |
117
+| RUSTDESK_API_GIN_TRUST_PROXY        | 信任的代理IP列表,以`,`分割,默认信任所有              | 192.168.1.2,192.168.1.3     |
105 118
 | -----------GORM配置------------------ | ------------------------------------ | --------------------------- |
106 119
 | RUSTDESK_API_GORM_TYPE              | 数据库类型sqlite或者mysql,默认sqlite          | sqlite                      |
107 120
 | RUSTDESK_API_GORM_MAX_IDLE_CONNS    | 数据库最大空闲连接数                           | 10                          |
108 121
 | RUSTDESK_API_GORM_MAX_OPEN_CONNS    | 数据库最大打开连接数                           | 100                         |
109
-| -----------MYSQL配置----------------- | --------数据库类型为sqlite时不用填-------      | ----------                  |
122
+| -----MYSQL配置-----                   | -----数据库类型为sqlite时不用填-----           | ----------                  |
110 123
 | RUSTDESK_API_MYSQL_USERNAME         | mysql用户名                             | root                        |
111 124
 | RUSTDESK_API_MYSQL_PASSWORD         | mysql密码                              | 111111                      |
112 125
 | RUSTDESK_API_MYSQL_ADDR             | mysql地址                              | 192.168.1.66:3306           |
113 126
 | RUSTDESK_API_MYSQL_DBNAME           | mysql数据库名                            | rustdesk                    |
114
-| -----------RUSTDESK配置-------------- | -----------------------------------  | ----------                  |
127
+| -----RUSTDESK配置-----                | ---------------                      | ----------                  |
115 128
 | RUSTDESK_API_RUSTDESK_ID_SERVER     | Rustdesk的id服务器地址                     | 192.168.1.66:21116          |
116 129
 | RUSTDESK_API_RUSTDESK_RELAY_SERVER  | Rustdesk的relay服务器地址                  | 192.168.1.66:21117          |
117 130
 | RUSTDESK_API_RUSTDESK_API_SERVER    | Rustdesk的api服务器地址                    | http://192.168.1.66:21114   |
118 131
 | RUSTDESK_API_RUSTDESK_KEY           | Rustdesk的key                         | 123456789                   |
119 132
 
120
-2. 使用`docker compose`,根据rustdesk提供的示例加上自己的rustdesk-api
133
+### 安装步骤
134
+
135
+#### docker运行
136
+
137
+1. 直接docker运行,配置可以通过挂载配置文件`/app/conf/config.yaml`来修改,或者通过环境变量覆盖配置文件中的配置
138
+
139
+```bash
140
+docker run -d --name rustdesk-api -p 21114:21114 \
141
+-v /data/rustdesk/api:/app/data \
142
+-e RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116 \
143
+-e RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117 \
144
+-e RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114 \
145
+-e RUSTDESK_API_RUSTDESK_KEY=123456789 \
146
+lejianwen/rustdesk-api
147
+```
148
+
149
+2. 使用`docker compose`
150
+
151
+- 简单示例
152
+
153
+```docker-compose
154
+services:
155
+   rustdesk-api:
156
+    container_name: rustdesk-api
157
+    environment:
158
+      - RUSTDESK_API_RUSTDESK_ID_SERVER=192.168.1.66:21116
159
+      - RUSTDESK_API_RUSTDESK_RELAY_SERVER=192.168.1.66:21117
160
+      - RUSTDESK_API_RUSTDESK_API_SERVER=http://192.168.1.66:21114
161
+      - RUSTDESK_API_RUSTDESK_KEY=123456789
162
+    ports:
163
+      - 21114:21114
164
+    image: lejianwen/rustdesk-api
165
+    volumes:
166
+      - /data/rustdesk/api:/app/data #将数据库挂载出来方便备份
167
+    networks:
168
+      - rustdesk-net
169
+    restart: unless-stopped
170
+```
171
+
172
+- 根据rustdesk提供的示例加上自己的rustdesk-api
121 173
 
122 174
 ```docker-compose
123 175
 networks:

+ 4 - 1
cmd/apimain.go

@@ -157,7 +157,7 @@ func ApiInitValidator() {
157 157
 }
158 158
 
159 159
 func DatabaseAutoUpdate() {
160
-	version := 100
160
+	version := 103
161 161
 
162 162
 	db := global.DB
163 163
 
@@ -215,6 +215,9 @@ func Migrate(version uint) {
215 215
 		&model.AddressBook{},
216 216
 		&model.Peer{},
217 217
 		&model.Group{},
218
+		&model.UserThird{},
219
+		&model.Oauth{},
220
+		&model.LoginLog{},
218 221
 	)
219 222
 	if err != nil {
220 223
 		fmt.Println("migrate err :=>", err)

+ 5 - 4
conf/config.yaml

@@ -2,6 +2,7 @@ gin:
2 2
   api-addr: "0.0.0.0:21114"
3 3
   mode: "release" #release,debug,test
4 4
   resources-path: 'resources'  #对外静态文件目录
5
+  trust-proxy: ""
5 6
 gorm:
6 7
   type: "sqlite"
7 8
   max-idle-conns: 10
@@ -16,14 +17,14 @@ rustdesk:
16 17
   relay-server: "192.168.1.66:21117"
17 18
   api-server: "http://192.168.1.66:21114"
18 19
   key: "123456789"
19
-redis:
20
-  addr: "127.0.0.1:6379"
21
-  password: ""
22
-  db: 0
23 20
 logger:
24 21
   path: "./runtime/log.txt"
25 22
   level: "error" #trace,debug,info,warn,error,fatal
26 23
   report-caller: true
24
+redis:
25
+  addr: "127.0.0.1:6379"
26
+  password: ""
27
+  db: 0
27 28
 cache:
28 29
   type: "file"
29 30
   file-dir: "./runtime/cache"

+ 1 - 0
config/gin.go

@@ -5,4 +5,5 @@ type Gin struct {
5 5
 	AdminAddr     string `mapstructure:"admin-addr"`
6 6
 	Mode          string
7 7
 	ResourcesPath string `mapstructure:"resources-path"`
8
+	TrustProxy    string `mapstructure:"trust-proxy"`
8 9
 }

+ 13 - 0
config/oauth.go

@@ -0,0 +1,13 @@
1
+package config
2
+
3
+type GithubOauth struct {
4
+	ClientId     string `mapstructure:"client-id"`
5
+	ClientSecret string `mapstructure:"client-secret"`
6
+	RedirectUrl  string `mapstructure:"redirect-url"`
7
+}
8
+
9
+type GoogleOauth struct {
10
+	ClientId     string `mapstructure:"client-id"`
11
+	ClientSecret string `mapstructure:"client-secret"`
12
+	RedirectUrl  string `mapstructure:"redirect-url"`
13
+}

+ 638 - 2
docs/admin/admin_docs.go

@@ -709,6 +709,172 @@ const docTemplateadmin = `{
709 709
                 }
710 710
             }
711 711
         },
712
+        "/admin/loginLog/delete": {
713
+            "post": {
714
+                "security": [
715
+                    {
716
+                        "token": []
717
+                    }
718
+                ],
719
+                "description": "登录日志删除",
720
+                "consumes": [
721
+                    "application/json"
722
+                ],
723
+                "produces": [
724
+                    "application/json"
725
+                ],
726
+                "tags": [
727
+                    "登录日志"
728
+                ],
729
+                "summary": "登录日志删除",
730
+                "parameters": [
731
+                    {
732
+                        "description": "登录日志信息",
733
+                        "name": "body",
734
+                        "in": "body",
735
+                        "required": true,
736
+                        "schema": {
737
+                            "$ref": "#/definitions/model.LoginLog"
738
+                        }
739
+                    }
740
+                ],
741
+                "responses": {
742
+                    "200": {
743
+                        "description": "OK",
744
+                        "schema": {
745
+                            "$ref": "#/definitions/response.Response"
746
+                        }
747
+                    },
748
+                    "500": {
749
+                        "description": "Internal Server Error",
750
+                        "schema": {
751
+                            "$ref": "#/definitions/response.Response"
752
+                        }
753
+                    }
754
+                }
755
+            }
756
+        },
757
+        "/admin/loginLog/detail/{id}": {
758
+            "get": {
759
+                "security": [
760
+                    {
761
+                        "token": []
762
+                    }
763
+                ],
764
+                "description": "登录日志详情",
765
+                "consumes": [
766
+                    "application/json"
767
+                ],
768
+                "produces": [
769
+                    "application/json"
770
+                ],
771
+                "tags": [
772
+                    "登录日志"
773
+                ],
774
+                "summary": "登录日志详情",
775
+                "parameters": [
776
+                    {
777
+                        "type": "integer",
778
+                        "description": "ID",
779
+                        "name": "id",
780
+                        "in": "path",
781
+                        "required": true
782
+                    }
783
+                ],
784
+                "responses": {
785
+                    "200": {
786
+                        "description": "OK",
787
+                        "schema": {
788
+                            "allOf": [
789
+                                {
790
+                                    "$ref": "#/definitions/response.Response"
791
+                                },
792
+                                {
793
+                                    "type": "object",
794
+                                    "properties": {
795
+                                        "data": {
796
+                                            "$ref": "#/definitions/model.LoginLog"
797
+                                        }
798
+                                    }
799
+                                }
800
+                            ]
801
+                        }
802
+                    },
803
+                    "500": {
804
+                        "description": "Internal Server Error",
805
+                        "schema": {
806
+                            "$ref": "#/definitions/response.Response"
807
+                        }
808
+                    }
809
+                }
810
+            }
811
+        },
812
+        "/admin/loginLog/list": {
813
+            "get": {
814
+                "security": [
815
+                    {
816
+                        "token": []
817
+                    }
818
+                ],
819
+                "description": "登录日志列表",
820
+                "consumes": [
821
+                    "application/json"
822
+                ],
823
+                "produces": [
824
+                    "application/json"
825
+                ],
826
+                "tags": [
827
+                    "登录日志"
828
+                ],
829
+                "summary": "登录日志列表",
830
+                "parameters": [
831
+                    {
832
+                        "type": "integer",
833
+                        "description": "页码",
834
+                        "name": "page",
835
+                        "in": "query"
836
+                    },
837
+                    {
838
+                        "type": "integer",
839
+                        "description": "页大小",
840
+                        "name": "page_size",
841
+                        "in": "query"
842
+                    },
843
+                    {
844
+                        "type": "integer",
845
+                        "description": "用户ID",
846
+                        "name": "user_id",
847
+                        "in": "query"
848
+                    }
849
+                ],
850
+                "responses": {
851
+                    "200": {
852
+                        "description": "OK",
853
+                        "schema": {
854
+                            "allOf": [
855
+                                {
856
+                                    "$ref": "#/definitions/response.Response"
857
+                                },
858
+                                {
859
+                                    "type": "object",
860
+                                    "properties": {
861
+                                        "data": {
862
+                                            "$ref": "#/definitions/model.LoginLogList"
863
+                                        }
864
+                                    }
865
+                                }
866
+                            ]
867
+                        }
868
+                    },
869
+                    "500": {
870
+                        "description": "Internal Server Error",
871
+                        "schema": {
872
+                            "$ref": "#/definitions/response.Response"
873
+                        }
874
+                    }
875
+                }
876
+            }
877
+        },
712 878
         "/admin/logout": {
713 879
             "post": {
714 880
                 "description": "登出",
@@ -719,15 +885,289 @@ const docTemplateadmin = `{
719 885
                     "application/json"
720 886
                 ],
721 887
                 "tags": [
722
-                    "登录"
888
+                    "登录"
889
+                ],
890
+                "summary": "登出",
891
+                "responses": {
892
+                    "200": {
893
+                        "description": "OK",
894
+                        "schema": {
895
+                            "$ref": "#/definitions/response.Response"
896
+                        }
897
+                    },
898
+                    "500": {
899
+                        "description": "Internal Server Error",
900
+                        "schema": {
901
+                            "$ref": "#/definitions/response.Response"
902
+                        }
903
+                    }
904
+                }
905
+            }
906
+        },
907
+        "/admin/oauth/create": {
908
+            "post": {
909
+                "security": [
910
+                    {
911
+                        "token": []
912
+                    }
913
+                ],
914
+                "description": "创建Oauth",
915
+                "consumes": [
916
+                    "application/json"
917
+                ],
918
+                "produces": [
919
+                    "application/json"
920
+                ],
921
+                "tags": [
922
+                    "Oauth"
923
+                ],
924
+                "summary": "创建Oauth",
925
+                "parameters": [
926
+                    {
927
+                        "description": "Oauth信息",
928
+                        "name": "body",
929
+                        "in": "body",
930
+                        "required": true,
931
+                        "schema": {
932
+                            "$ref": "#/definitions/admin.OauthForm"
933
+                        }
934
+                    }
935
+                ],
936
+                "responses": {
937
+                    "200": {
938
+                        "description": "OK",
939
+                        "schema": {
940
+                            "allOf": [
941
+                                {
942
+                                    "$ref": "#/definitions/response.Response"
943
+                                },
944
+                                {
945
+                                    "type": "object",
946
+                                    "properties": {
947
+                                        "data": {
948
+                                            "$ref": "#/definitions/model.Oauth"
949
+                                        }
950
+                                    }
951
+                                }
952
+                            ]
953
+                        }
954
+                    },
955
+                    "500": {
956
+                        "description": "Internal Server Error",
957
+                        "schema": {
958
+                            "$ref": "#/definitions/response.Response"
959
+                        }
960
+                    }
961
+                }
962
+            }
963
+        },
964
+        "/admin/oauth/delete": {
965
+            "post": {
966
+                "security": [
967
+                    {
968
+                        "token": []
969
+                    }
970
+                ],
971
+                "description": "Oauth删除",
972
+                "consumes": [
973
+                    "application/json"
974
+                ],
975
+                "produces": [
976
+                    "application/json"
977
+                ],
978
+                "tags": [
979
+                    "Oauth"
980
+                ],
981
+                "summary": "Oauth删除",
982
+                "parameters": [
983
+                    {
984
+                        "description": "Oauth信息",
985
+                        "name": "body",
986
+                        "in": "body",
987
+                        "required": true,
988
+                        "schema": {
989
+                            "$ref": "#/definitions/admin.OauthForm"
990
+                        }
991
+                    }
992
+                ],
993
+                "responses": {
994
+                    "200": {
995
+                        "description": "OK",
996
+                        "schema": {
997
+                            "$ref": "#/definitions/response.Response"
998
+                        }
999
+                    },
1000
+                    "500": {
1001
+                        "description": "Internal Server Error",
1002
+                        "schema": {
1003
+                            "$ref": "#/definitions/response.Response"
1004
+                        }
1005
+                    }
1006
+                }
1007
+            }
1008
+        },
1009
+        "/admin/oauth/detail/{id}": {
1010
+            "get": {
1011
+                "security": [
1012
+                    {
1013
+                        "token": []
1014
+                    }
1015
+                ],
1016
+                "description": "Oauth详情",
1017
+                "consumes": [
1018
+                    "application/json"
1019
+                ],
1020
+                "produces": [
1021
+                    "application/json"
1022
+                ],
1023
+                "tags": [
1024
+                    "Oauth"
1025
+                ],
1026
+                "summary": "Oauth详情",
1027
+                "parameters": [
1028
+                    {
1029
+                        "type": "integer",
1030
+                        "description": "ID",
1031
+                        "name": "id",
1032
+                        "in": "path",
1033
+                        "required": true
1034
+                    }
1035
+                ],
1036
+                "responses": {
1037
+                    "200": {
1038
+                        "description": "OK",
1039
+                        "schema": {
1040
+                            "allOf": [
1041
+                                {
1042
+                                    "$ref": "#/definitions/response.Response"
1043
+                                },
1044
+                                {
1045
+                                    "type": "object",
1046
+                                    "properties": {
1047
+                                        "data": {
1048
+                                            "$ref": "#/definitions/model.Oauth"
1049
+                                        }
1050
+                                    }
1051
+                                }
1052
+                            ]
1053
+                        }
1054
+                    },
1055
+                    "500": {
1056
+                        "description": "Internal Server Error",
1057
+                        "schema": {
1058
+                            "$ref": "#/definitions/response.Response"
1059
+                        }
1060
+                    }
1061
+                }
1062
+            }
1063
+        },
1064
+        "/admin/oauth/list": {
1065
+            "get": {
1066
+                "security": [
1067
+                    {
1068
+                        "token": []
1069
+                    }
1070
+                ],
1071
+                "description": "Oauth列表",
1072
+                "consumes": [
1073
+                    "application/json"
1074
+                ],
1075
+                "produces": [
1076
+                    "application/json"
1077
+                ],
1078
+                "tags": [
1079
+                    "Oauth"
1080
+                ],
1081
+                "summary": "Oauth列表",
1082
+                "parameters": [
1083
+                    {
1084
+                        "type": "integer",
1085
+                        "description": "页码",
1086
+                        "name": "page",
1087
+                        "in": "query"
1088
+                    },
1089
+                    {
1090
+                        "type": "integer",
1091
+                        "description": "页大小",
1092
+                        "name": "page_size",
1093
+                        "in": "query"
1094
+                    }
723 1095
                 ],
724
-                "summary": "登出",
725 1096
                 "responses": {
726 1097
                     "200": {
727 1098
                         "description": "OK",
1099
+                        "schema": {
1100
+                            "allOf": [
1101
+                                {
1102
+                                    "$ref": "#/definitions/response.Response"
1103
+                                },
1104
+                                {
1105
+                                    "type": "object",
1106
+                                    "properties": {
1107
+                                        "data": {
1108
+                                            "$ref": "#/definitions/model.OauthList"
1109
+                                        }
1110
+                                    }
1111
+                                }
1112
+                            ]
1113
+                        }
1114
+                    },
1115
+                    "500": {
1116
+                        "description": "Internal Server Error",
728 1117
                         "schema": {
729 1118
                             "$ref": "#/definitions/response.Response"
730 1119
                         }
1120
+                    }
1121
+                }
1122
+            }
1123
+        },
1124
+        "/admin/oauth/update": {
1125
+            "post": {
1126
+                "security": [
1127
+                    {
1128
+                        "token": []
1129
+                    }
1130
+                ],
1131
+                "description": "Oauth编辑",
1132
+                "consumes": [
1133
+                    "application/json"
1134
+                ],
1135
+                "produces": [
1136
+                    "application/json"
1137
+                ],
1138
+                "tags": [
1139
+                    "Oauth"
1140
+                ],
1141
+                "summary": "Oauth编辑",
1142
+                "parameters": [
1143
+                    {
1144
+                        "description": "Oauth信息",
1145
+                        "name": "body",
1146
+                        "in": "body",
1147
+                        "required": true,
1148
+                        "schema": {
1149
+                            "$ref": "#/definitions/admin.OauthForm"
1150
+                        }
1151
+                    }
1152
+                ],
1153
+                "responses": {
1154
+                    "200": {
1155
+                        "description": "OK",
1156
+                        "schema": {
1157
+                            "allOf": [
1158
+                                {
1159
+                                    "$ref": "#/definitions/response.Response"
1160
+                                },
1161
+                                {
1162
+                                    "type": "object",
1163
+                                    "properties": {
1164
+                                        "data": {
1165
+                                            "$ref": "#/definitions/model.OauthList"
1166
+                                        }
1167
+                                    }
1168
+                                }
1169
+                            ]
1170
+                        }
731 1171
                     },
732 1172
                     "500": {
733 1173
                         "description": "Internal Server Error",
@@ -1646,6 +2086,55 @@ const docTemplateadmin = `{
1646 2086
                 }
1647 2087
             }
1648 2088
         },
2089
+        "/admin/user/myOauth": {
2090
+            "get": {
2091
+                "security": [
2092
+                    {
2093
+                        "token": []
2094
+                    }
2095
+                ],
2096
+                "description": "我的授权",
2097
+                "consumes": [
2098
+                    "application/json"
2099
+                ],
2100
+                "produces": [
2101
+                    "application/json"
2102
+                ],
2103
+                "tags": [
2104
+                    "用户"
2105
+                ],
2106
+                "summary": "我的授权",
2107
+                "responses": {
2108
+                    "200": {
2109
+                        "description": "OK",
2110
+                        "schema": {
2111
+                            "allOf": [
2112
+                                {
2113
+                                    "$ref": "#/definitions/response.Response"
2114
+                                },
2115
+                                {
2116
+                                    "type": "object",
2117
+                                    "properties": {
2118
+                                        "data": {
2119
+                                            "type": "array",
2120
+                                            "items": {
2121
+                                                "$ref": "#/definitions/admin.UserOauthItem"
2122
+                                            }
2123
+                                        }
2124
+                                    }
2125
+                                }
2126
+                            ]
2127
+                        }
2128
+                    },
2129
+                    "500": {
2130
+                        "description": "Internal Server Error",
2131
+                        "schema": {
2132
+                            "$ref": "#/definitions/response.Response"
2133
+                        }
2134
+                    }
2135
+                }
2136
+            }
2137
+        },
1649 2138
         "/admin/user/update": {
1650 2139
             "post": {
1651 2140
                 "security": [
@@ -1760,6 +2249,9 @@ const docTemplateadmin = `{
1760 2249
                 "password": {
1761 2250
                     "type": "string"
1762 2251
                 },
2252
+                "platform": {
2253
+                    "type": "string"
2254
+                },
1763 2255
                 "username": {
1764 2256
                     "type": "string"
1765 2257
                 }
@@ -1877,6 +2369,35 @@ const docTemplateadmin = `{
1877 2369
                 }
1878 2370
             }
1879 2371
         },
2372
+        "admin.OauthForm": {
2373
+            "type": "object",
2374
+            "required": [
2375
+                "client_id",
2376
+                "client_secret",
2377
+                "op",
2378
+                "redirect_url"
2379
+            ],
2380
+            "properties": {
2381
+                "auto_register": {
2382
+                    "type": "boolean"
2383
+                },
2384
+                "client_id": {
2385
+                    "type": "string"
2386
+                },
2387
+                "client_secret": {
2388
+                    "type": "string"
2389
+                },
2390
+                "id": {
2391
+                    "type": "integer"
2392
+                },
2393
+                "op": {
2394
+                    "type": "string"
2395
+                },
2396
+                "redirect_url": {
2397
+                    "type": "string"
2398
+                }
2399
+            }
2400
+        },
1880 2401
         "admin.PeerForm": {
1881 2402
             "type": "object",
1882 2403
             "properties": {
@@ -1971,6 +2492,17 @@ const docTemplateadmin = `{
1971 2492
                 }
1972 2493
             }
1973 2494
         },
2495
+        "admin.UserOauthItem": {
2496
+            "type": "object",
2497
+            "properties": {
2498
+                "status": {
2499
+                    "type": "integer"
2500
+                },
2501
+                "third_type": {
2502
+                    "type": "string"
2503
+                }
2504
+            }
2505
+        },
1974 2506
         "admin.UserPasswordForm": {
1975 2507
             "type": "object",
1976 2508
             "required": [
@@ -2110,6 +2642,110 @@ const docTemplateadmin = `{
2110 2642
                 }
2111 2643
             }
2112 2644
         },
2645
+        "model.LoginLog": {
2646
+            "type": "object",
2647
+            "properties": {
2648
+                "client": {
2649
+                    "description": "webadmin,webclient,app,",
2650
+                    "type": "string"
2651
+                },
2652
+                "created_at": {
2653
+                    "type": "string"
2654
+                },
2655
+                "id": {
2656
+                    "type": "integer"
2657
+                },
2658
+                "ip": {
2659
+                    "type": "string"
2660
+                },
2661
+                "platform": {
2662
+                    "description": "windows,linux,mac,android,ios",
2663
+                    "type": "string"
2664
+                },
2665
+                "type": {
2666
+                    "description": "account,oauth",
2667
+                    "type": "string"
2668
+                },
2669
+                "updated_at": {
2670
+                    "type": "string"
2671
+                },
2672
+                "user_id": {
2673
+                    "type": "integer"
2674
+                },
2675
+                "uuid": {
2676
+                    "type": "string"
2677
+                }
2678
+            }
2679
+        },
2680
+        "model.LoginLogList": {
2681
+            "type": "object",
2682
+            "properties": {
2683
+                "list": {
2684
+                    "type": "array",
2685
+                    "items": {
2686
+                        "$ref": "#/definitions/model.LoginLog"
2687
+                    }
2688
+                },
2689
+                "page": {
2690
+                    "type": "integer"
2691
+                },
2692
+                "page_size": {
2693
+                    "type": "integer"
2694
+                },
2695
+                "total": {
2696
+                    "type": "integer"
2697
+                }
2698
+            }
2699
+        },
2700
+        "model.Oauth": {
2701
+            "type": "object",
2702
+            "properties": {
2703
+                "auto_register": {
2704
+                    "type": "boolean"
2705
+                },
2706
+                "client_id": {
2707
+                    "type": "string"
2708
+                },
2709
+                "client_secret": {
2710
+                    "type": "string"
2711
+                },
2712
+                "created_at": {
2713
+                    "type": "string"
2714
+                },
2715
+                "id": {
2716
+                    "type": "integer"
2717
+                },
2718
+                "op": {
2719
+                    "type": "string"
2720
+                },
2721
+                "redirect_url": {
2722
+                    "type": "string"
2723
+                },
2724
+                "updated_at": {
2725
+                    "type": "string"
2726
+                }
2727
+            }
2728
+        },
2729
+        "model.OauthList": {
2730
+            "type": "object",
2731
+            "properties": {
2732
+                "list": {
2733
+                    "type": "array",
2734
+                    "items": {
2735
+                        "$ref": "#/definitions/model.Oauth"
2736
+                    }
2737
+                },
2738
+                "page": {
2739
+                    "type": "integer"
2740
+                },
2741
+                "page_size": {
2742
+                    "type": "integer"
2743
+                },
2744
+                "total": {
2745
+                    "type": "integer"
2746
+                }
2747
+            }
2748
+        },
2113 2749
         "model.Peer": {
2114 2750
             "type": "object",
2115 2751
             "properties": {

+ 638 - 2
docs/admin/admin_swagger.json

@@ -702,6 +702,172 @@
702 702
                 }
703 703
             }
704 704
         },
705
+        "/admin/loginLog/delete": {
706
+            "post": {
707
+                "security": [
708
+                    {
709
+                        "token": []
710
+                    }
711
+                ],
712
+                "description": "登录日志删除",
713
+                "consumes": [
714
+                    "application/json"
715
+                ],
716
+                "produces": [
717
+                    "application/json"
718
+                ],
719
+                "tags": [
720
+                    "登录日志"
721
+                ],
722
+                "summary": "登录日志删除",
723
+                "parameters": [
724
+                    {
725
+                        "description": "登录日志信息",
726
+                        "name": "body",
727
+                        "in": "body",
728
+                        "required": true,
729
+                        "schema": {
730
+                            "$ref": "#/definitions/model.LoginLog"
731
+                        }
732
+                    }
733
+                ],
734
+                "responses": {
735
+                    "200": {
736
+                        "description": "OK",
737
+                        "schema": {
738
+                            "$ref": "#/definitions/response.Response"
739
+                        }
740
+                    },
741
+                    "500": {
742
+                        "description": "Internal Server Error",
743
+                        "schema": {
744
+                            "$ref": "#/definitions/response.Response"
745
+                        }
746
+                    }
747
+                }
748
+            }
749
+        },
750
+        "/admin/loginLog/detail/{id}": {
751
+            "get": {
752
+                "security": [
753
+                    {
754
+                        "token": []
755
+                    }
756
+                ],
757
+                "description": "登录日志详情",
758
+                "consumes": [
759
+                    "application/json"
760
+                ],
761
+                "produces": [
762
+                    "application/json"
763
+                ],
764
+                "tags": [
765
+                    "登录日志"
766
+                ],
767
+                "summary": "登录日志详情",
768
+                "parameters": [
769
+                    {
770
+                        "type": "integer",
771
+                        "description": "ID",
772
+                        "name": "id",
773
+                        "in": "path",
774
+                        "required": true
775
+                    }
776
+                ],
777
+                "responses": {
778
+                    "200": {
779
+                        "description": "OK",
780
+                        "schema": {
781
+                            "allOf": [
782
+                                {
783
+                                    "$ref": "#/definitions/response.Response"
784
+                                },
785
+                                {
786
+                                    "type": "object",
787
+                                    "properties": {
788
+                                        "data": {
789
+                                            "$ref": "#/definitions/model.LoginLog"
790
+                                        }
791
+                                    }
792
+                                }
793
+                            ]
794
+                        }
795
+                    },
796
+                    "500": {
797
+                        "description": "Internal Server Error",
798
+                        "schema": {
799
+                            "$ref": "#/definitions/response.Response"
800
+                        }
801
+                    }
802
+                }
803
+            }
804
+        },
805
+        "/admin/loginLog/list": {
806
+            "get": {
807
+                "security": [
808
+                    {
809
+                        "token": []
810
+                    }
811
+                ],
812
+                "description": "登录日志列表",
813
+                "consumes": [
814
+                    "application/json"
815
+                ],
816
+                "produces": [
817
+                    "application/json"
818
+                ],
819
+                "tags": [
820
+                    "登录日志"
821
+                ],
822
+                "summary": "登录日志列表",
823
+                "parameters": [
824
+                    {
825
+                        "type": "integer",
826
+                        "description": "页码",
827
+                        "name": "page",
828
+                        "in": "query"
829
+                    },
830
+                    {
831
+                        "type": "integer",
832
+                        "description": "页大小",
833
+                        "name": "page_size",
834
+                        "in": "query"
835
+                    },
836
+                    {
837
+                        "type": "integer",
838
+                        "description": "用户ID",
839
+                        "name": "user_id",
840
+                        "in": "query"
841
+                    }
842
+                ],
843
+                "responses": {
844
+                    "200": {
845
+                        "description": "OK",
846
+                        "schema": {
847
+                            "allOf": [
848
+                                {
849
+                                    "$ref": "#/definitions/response.Response"
850
+                                },
851
+                                {
852
+                                    "type": "object",
853
+                                    "properties": {
854
+                                        "data": {
855
+                                            "$ref": "#/definitions/model.LoginLogList"
856
+                                        }
857
+                                    }
858
+                                }
859
+                            ]
860
+                        }
861
+                    },
862
+                    "500": {
863
+                        "description": "Internal Server Error",
864
+                        "schema": {
865
+                            "$ref": "#/definitions/response.Response"
866
+                        }
867
+                    }
868
+                }
869
+            }
870
+        },
705 871
         "/admin/logout": {
706 872
             "post": {
707 873
                 "description": "登出",
@@ -712,15 +878,289 @@
712 878
                     "application/json"
713 879
                 ],
714 880
                 "tags": [
715
-                    "登录"
881
+                    "登录"
882
+                ],
883
+                "summary": "登出",
884
+                "responses": {
885
+                    "200": {
886
+                        "description": "OK",
887
+                        "schema": {
888
+                            "$ref": "#/definitions/response.Response"
889
+                        }
890
+                    },
891
+                    "500": {
892
+                        "description": "Internal Server Error",
893
+                        "schema": {
894
+                            "$ref": "#/definitions/response.Response"
895
+                        }
896
+                    }
897
+                }
898
+            }
899
+        },
900
+        "/admin/oauth/create": {
901
+            "post": {
902
+                "security": [
903
+                    {
904
+                        "token": []
905
+                    }
906
+                ],
907
+                "description": "创建Oauth",
908
+                "consumes": [
909
+                    "application/json"
910
+                ],
911
+                "produces": [
912
+                    "application/json"
913
+                ],
914
+                "tags": [
915
+                    "Oauth"
916
+                ],
917
+                "summary": "创建Oauth",
918
+                "parameters": [
919
+                    {
920
+                        "description": "Oauth信息",
921
+                        "name": "body",
922
+                        "in": "body",
923
+                        "required": true,
924
+                        "schema": {
925
+                            "$ref": "#/definitions/admin.OauthForm"
926
+                        }
927
+                    }
928
+                ],
929
+                "responses": {
930
+                    "200": {
931
+                        "description": "OK",
932
+                        "schema": {
933
+                            "allOf": [
934
+                                {
935
+                                    "$ref": "#/definitions/response.Response"
936
+                                },
937
+                                {
938
+                                    "type": "object",
939
+                                    "properties": {
940
+                                        "data": {
941
+                                            "$ref": "#/definitions/model.Oauth"
942
+                                        }
943
+                                    }
944
+                                }
945
+                            ]
946
+                        }
947
+                    },
948
+                    "500": {
949
+                        "description": "Internal Server Error",
950
+                        "schema": {
951
+                            "$ref": "#/definitions/response.Response"
952
+                        }
953
+                    }
954
+                }
955
+            }
956
+        },
957
+        "/admin/oauth/delete": {
958
+            "post": {
959
+                "security": [
960
+                    {
961
+                        "token": []
962
+                    }
963
+                ],
964
+                "description": "Oauth删除",
965
+                "consumes": [
966
+                    "application/json"
967
+                ],
968
+                "produces": [
969
+                    "application/json"
970
+                ],
971
+                "tags": [
972
+                    "Oauth"
973
+                ],
974
+                "summary": "Oauth删除",
975
+                "parameters": [
976
+                    {
977
+                        "description": "Oauth信息",
978
+                        "name": "body",
979
+                        "in": "body",
980
+                        "required": true,
981
+                        "schema": {
982
+                            "$ref": "#/definitions/admin.OauthForm"
983
+                        }
984
+                    }
985
+                ],
986
+                "responses": {
987
+                    "200": {
988
+                        "description": "OK",
989
+                        "schema": {
990
+                            "$ref": "#/definitions/response.Response"
991
+                        }
992
+                    },
993
+                    "500": {
994
+                        "description": "Internal Server Error",
995
+                        "schema": {
996
+                            "$ref": "#/definitions/response.Response"
997
+                        }
998
+                    }
999
+                }
1000
+            }
1001
+        },
1002
+        "/admin/oauth/detail/{id}": {
1003
+            "get": {
1004
+                "security": [
1005
+                    {
1006
+                        "token": []
1007
+                    }
1008
+                ],
1009
+                "description": "Oauth详情",
1010
+                "consumes": [
1011
+                    "application/json"
1012
+                ],
1013
+                "produces": [
1014
+                    "application/json"
1015
+                ],
1016
+                "tags": [
1017
+                    "Oauth"
1018
+                ],
1019
+                "summary": "Oauth详情",
1020
+                "parameters": [
1021
+                    {
1022
+                        "type": "integer",
1023
+                        "description": "ID",
1024
+                        "name": "id",
1025
+                        "in": "path",
1026
+                        "required": true
1027
+                    }
1028
+                ],
1029
+                "responses": {
1030
+                    "200": {
1031
+                        "description": "OK",
1032
+                        "schema": {
1033
+                            "allOf": [
1034
+                                {
1035
+                                    "$ref": "#/definitions/response.Response"
1036
+                                },
1037
+                                {
1038
+                                    "type": "object",
1039
+                                    "properties": {
1040
+                                        "data": {
1041
+                                            "$ref": "#/definitions/model.Oauth"
1042
+                                        }
1043
+                                    }
1044
+                                }
1045
+                            ]
1046
+                        }
1047
+                    },
1048
+                    "500": {
1049
+                        "description": "Internal Server Error",
1050
+                        "schema": {
1051
+                            "$ref": "#/definitions/response.Response"
1052
+                        }
1053
+                    }
1054
+                }
1055
+            }
1056
+        },
1057
+        "/admin/oauth/list": {
1058
+            "get": {
1059
+                "security": [
1060
+                    {
1061
+                        "token": []
1062
+                    }
1063
+                ],
1064
+                "description": "Oauth列表",
1065
+                "consumes": [
1066
+                    "application/json"
1067
+                ],
1068
+                "produces": [
1069
+                    "application/json"
1070
+                ],
1071
+                "tags": [
1072
+                    "Oauth"
1073
+                ],
1074
+                "summary": "Oauth列表",
1075
+                "parameters": [
1076
+                    {
1077
+                        "type": "integer",
1078
+                        "description": "页码",
1079
+                        "name": "page",
1080
+                        "in": "query"
1081
+                    },
1082
+                    {
1083
+                        "type": "integer",
1084
+                        "description": "页大小",
1085
+                        "name": "page_size",
1086
+                        "in": "query"
1087
+                    }
716 1088
                 ],
717
-                "summary": "登出",
718 1089
                 "responses": {
719 1090
                     "200": {
720 1091
                         "description": "OK",
1092
+                        "schema": {
1093
+                            "allOf": [
1094
+                                {
1095
+                                    "$ref": "#/definitions/response.Response"
1096
+                                },
1097
+                                {
1098
+                                    "type": "object",
1099
+                                    "properties": {
1100
+                                        "data": {
1101
+                                            "$ref": "#/definitions/model.OauthList"
1102
+                                        }
1103
+                                    }
1104
+                                }
1105
+                            ]
1106
+                        }
1107
+                    },
1108
+                    "500": {
1109
+                        "description": "Internal Server Error",
721 1110
                         "schema": {
722 1111
                             "$ref": "#/definitions/response.Response"
723 1112
                         }
1113
+                    }
1114
+                }
1115
+            }
1116
+        },
1117
+        "/admin/oauth/update": {
1118
+            "post": {
1119
+                "security": [
1120
+                    {
1121
+                        "token": []
1122
+                    }
1123
+                ],
1124
+                "description": "Oauth编辑",
1125
+                "consumes": [
1126
+                    "application/json"
1127
+                ],
1128
+                "produces": [
1129
+                    "application/json"
1130
+                ],
1131
+                "tags": [
1132
+                    "Oauth"
1133
+                ],
1134
+                "summary": "Oauth编辑",
1135
+                "parameters": [
1136
+                    {
1137
+                        "description": "Oauth信息",
1138
+                        "name": "body",
1139
+                        "in": "body",
1140
+                        "required": true,
1141
+                        "schema": {
1142
+                            "$ref": "#/definitions/admin.OauthForm"
1143
+                        }
1144
+                    }
1145
+                ],
1146
+                "responses": {
1147
+                    "200": {
1148
+                        "description": "OK",
1149
+                        "schema": {
1150
+                            "allOf": [
1151
+                                {
1152
+                                    "$ref": "#/definitions/response.Response"
1153
+                                },
1154
+                                {
1155
+                                    "type": "object",
1156
+                                    "properties": {
1157
+                                        "data": {
1158
+                                            "$ref": "#/definitions/model.OauthList"
1159
+                                        }
1160
+                                    }
1161
+                                }
1162
+                            ]
1163
+                        }
724 1164
                     },
725 1165
                     "500": {
726 1166
                         "description": "Internal Server Error",
@@ -1639,6 +2079,55 @@
1639 2079
                 }
1640 2080
             }
1641 2081
         },
2082
+        "/admin/user/myOauth": {
2083
+            "get": {
2084
+                "security": [
2085
+                    {
2086
+                        "token": []
2087
+                    }
2088
+                ],
2089
+                "description": "我的授权",
2090
+                "consumes": [
2091
+                    "application/json"
2092
+                ],
2093
+                "produces": [
2094
+                    "application/json"
2095
+                ],
2096
+                "tags": [
2097
+                    "用户"
2098
+                ],
2099
+                "summary": "我的授权",
2100
+                "responses": {
2101
+                    "200": {
2102
+                        "description": "OK",
2103
+                        "schema": {
2104
+                            "allOf": [
2105
+                                {
2106
+                                    "$ref": "#/definitions/response.Response"
2107
+                                },
2108
+                                {
2109
+                                    "type": "object",
2110
+                                    "properties": {
2111
+                                        "data": {
2112
+                                            "type": "array",
2113
+                                            "items": {
2114
+                                                "$ref": "#/definitions/admin.UserOauthItem"
2115
+                                            }
2116
+                                        }
2117
+                                    }
2118
+                                }
2119
+                            ]
2120
+                        }
2121
+                    },
2122
+                    "500": {
2123
+                        "description": "Internal Server Error",
2124
+                        "schema": {
2125
+                            "$ref": "#/definitions/response.Response"
2126
+                        }
2127
+                    }
2128
+                }
2129
+            }
2130
+        },
1642 2131
         "/admin/user/update": {
1643 2132
             "post": {
1644 2133
                 "security": [
@@ -1753,6 +2242,9 @@
1753 2242
                 "password": {
1754 2243
                     "type": "string"
1755 2244
                 },
2245
+                "platform": {
2246
+                    "type": "string"
2247
+                },
1756 2248
                 "username": {
1757 2249
                     "type": "string"
1758 2250
                 }
@@ -1870,6 +2362,35 @@
1870 2362
                 }
1871 2363
             }
1872 2364
         },
2365
+        "admin.OauthForm": {
2366
+            "type": "object",
2367
+            "required": [
2368
+                "client_id",
2369
+                "client_secret",
2370
+                "op",
2371
+                "redirect_url"
2372
+            ],
2373
+            "properties": {
2374
+                "auto_register": {
2375
+                    "type": "boolean"
2376
+                },
2377
+                "client_id": {
2378
+                    "type": "string"
2379
+                },
2380
+                "client_secret": {
2381
+                    "type": "string"
2382
+                },
2383
+                "id": {
2384
+                    "type": "integer"
2385
+                },
2386
+                "op": {
2387
+                    "type": "string"
2388
+                },
2389
+                "redirect_url": {
2390
+                    "type": "string"
2391
+                }
2392
+            }
2393
+        },
1873 2394
         "admin.PeerForm": {
1874 2395
             "type": "object",
1875 2396
             "properties": {
@@ -1964,6 +2485,17 @@
1964 2485
                 }
1965 2486
             }
1966 2487
         },
2488
+        "admin.UserOauthItem": {
2489
+            "type": "object",
2490
+            "properties": {
2491
+                "status": {
2492
+                    "type": "integer"
2493
+                },
2494
+                "third_type": {
2495
+                    "type": "string"
2496
+                }
2497
+            }
2498
+        },
1967 2499
         "admin.UserPasswordForm": {
1968 2500
             "type": "object",
1969 2501
             "required": [
@@ -2103,6 +2635,110 @@
2103 2635
                 }
2104 2636
             }
2105 2637
         },
2638
+        "model.LoginLog": {
2639
+            "type": "object",
2640
+            "properties": {
2641
+                "client": {
2642
+                    "description": "webadmin,webclient,app,",
2643
+                    "type": "string"
2644
+                },
2645
+                "created_at": {
2646
+                    "type": "string"
2647
+                },
2648
+                "id": {
2649
+                    "type": "integer"
2650
+                },
2651
+                "ip": {
2652
+                    "type": "string"
2653
+                },
2654
+                "platform": {
2655
+                    "description": "windows,linux,mac,android,ios",
2656
+                    "type": "string"
2657
+                },
2658
+                "type": {
2659
+                    "description": "account,oauth",
2660
+                    "type": "string"
2661
+                },
2662
+                "updated_at": {
2663
+                    "type": "string"
2664
+                },
2665
+                "user_id": {
2666
+                    "type": "integer"
2667
+                },
2668
+                "uuid": {
2669
+                    "type": "string"
2670
+                }
2671
+            }
2672
+        },
2673
+        "model.LoginLogList": {
2674
+            "type": "object",
2675
+            "properties": {
2676
+                "list": {
2677
+                    "type": "array",
2678
+                    "items": {
2679
+                        "$ref": "#/definitions/model.LoginLog"
2680
+                    }
2681
+                },
2682
+                "page": {
2683
+                    "type": "integer"
2684
+                },
2685
+                "page_size": {
2686
+                    "type": "integer"
2687
+                },
2688
+                "total": {
2689
+                    "type": "integer"
2690
+                }
2691
+            }
2692
+        },
2693
+        "model.Oauth": {
2694
+            "type": "object",
2695
+            "properties": {
2696
+                "auto_register": {
2697
+                    "type": "boolean"
2698
+                },
2699
+                "client_id": {
2700
+                    "type": "string"
2701
+                },
2702
+                "client_secret": {
2703
+                    "type": "string"
2704
+                },
2705
+                "created_at": {
2706
+                    "type": "string"
2707
+                },
2708
+                "id": {
2709
+                    "type": "integer"
2710
+                },
2711
+                "op": {
2712
+                    "type": "string"
2713
+                },
2714
+                "redirect_url": {
2715
+                    "type": "string"
2716
+                },
2717
+                "updated_at": {
2718
+                    "type": "string"
2719
+                }
2720
+            }
2721
+        },
2722
+        "model.OauthList": {
2723
+            "type": "object",
2724
+            "properties": {
2725
+                "list": {
2726
+                    "type": "array",
2727
+                    "items": {
2728
+                        "$ref": "#/definitions/model.Oauth"
2729
+                    }
2730
+                },
2731
+                "page": {
2732
+                    "type": "integer"
2733
+                },
2734
+                "page_size": {
2735
+                    "type": "integer"
2736
+                },
2737
+                "total": {
2738
+                    "type": "integer"
2739
+                }
2740
+            }
2741
+        },
2106 2742
         "model.Peer": {
2107 2743
             "type": "object",
2108 2744
             "properties": {

+ 386 - 0
docs/admin/admin_swagger.yaml

@@ -4,6 +4,8 @@ definitions:
4 4
     properties:
5 5
       password:
6 6
         type: string
7
+      platform:
8
+        type: string
7 9
       username:
8 10
         type: string
9 11
     required:
@@ -85,6 +87,26 @@ definitions:
85 87
       username:
86 88
         type: string
87 89
     type: object
90
+  admin.OauthForm:
91
+    properties:
92
+      auto_register:
93
+        type: boolean
94
+      client_id:
95
+        type: string
96
+      client_secret:
97
+        type: string
98
+      id:
99
+        type: integer
100
+      op:
101
+        type: string
102
+      redirect_url:
103
+        type: string
104
+    required:
105
+    - client_id
106
+    - client_secret
107
+    - op
108
+    - redirect_url
109
+    type: object
88 110
   admin.PeerForm:
89 111
     properties:
90 112
       cpu:
@@ -148,6 +170,13 @@ definitions:
148 170
     - status
149 171
     - username
150 172
     type: object
173
+  admin.UserOauthItem:
174
+    properties:
175
+      status:
176
+        type: integer
177
+      third_type:
178
+        type: string
179
+    type: object
151 180
   admin.UserPasswordForm:
152 181
     properties:
153 182
       id:
@@ -240,6 +269,75 @@ definitions:
240 269
       total:
241 270
         type: integer
242 271
     type: object
272
+  model.LoginLog:
273
+    properties:
274
+      client:
275
+        description: webadmin,webclient,app,
276
+        type: string
277
+      created_at:
278
+        type: string
279
+      id:
280
+        type: integer
281
+      ip:
282
+        type: string
283
+      platform:
284
+        description: windows,linux,mac,android,ios
285
+        type: string
286
+      type:
287
+        description: account,oauth
288
+        type: string
289
+      updated_at:
290
+        type: string
291
+      user_id:
292
+        type: integer
293
+      uuid:
294
+        type: string
295
+    type: object
296
+  model.LoginLogList:
297
+    properties:
298
+      list:
299
+        items:
300
+          $ref: '#/definitions/model.LoginLog'
301
+        type: array
302
+      page:
303
+        type: integer
304
+      page_size:
305
+        type: integer
306
+      total:
307
+        type: integer
308
+    type: object
309
+  model.Oauth:
310
+    properties:
311
+      auto_register:
312
+        type: boolean
313
+      client_id:
314
+        type: string
315
+      client_secret:
316
+        type: string
317
+      created_at:
318
+        type: string
319
+      id:
320
+        type: integer
321
+      op:
322
+        type: string
323
+      redirect_url:
324
+        type: string
325
+      updated_at:
326
+        type: string
327
+    type: object
328
+  model.OauthList:
329
+    properties:
330
+      list:
331
+        items:
332
+          $ref: '#/definitions/model.Oauth'
333
+        type: array
334
+      page:
335
+        type: integer
336
+      page_size:
337
+        type: integer
338
+      total:
339
+        type: integer
340
+    type: object
243 341
   model.Peer:
244 342
     properties:
245 343
       cpu:
@@ -782,6 +880,105 @@ paths:
782 880
       summary: 登录
783 881
       tags:
784 882
       - 登录
883
+  /admin/loginLog/delete:
884
+    post:
885
+      consumes:
886
+      - application/json
887
+      description: 登录日志删除
888
+      parameters:
889
+      - description: 登录日志信息
890
+        in: body
891
+        name: body
892
+        required: true
893
+        schema:
894
+          $ref: '#/definitions/model.LoginLog'
895
+      produces:
896
+      - application/json
897
+      responses:
898
+        "200":
899
+          description: OK
900
+          schema:
901
+            $ref: '#/definitions/response.Response'
902
+        "500":
903
+          description: Internal Server Error
904
+          schema:
905
+            $ref: '#/definitions/response.Response'
906
+      security:
907
+      - token: []
908
+      summary: 登录日志删除
909
+      tags:
910
+      - 登录日志
911
+  /admin/loginLog/detail/{id}:
912
+    get:
913
+      consumes:
914
+      - application/json
915
+      description: 登录日志详情
916
+      parameters:
917
+      - description: ID
918
+        in: path
919
+        name: id
920
+        required: true
921
+        type: integer
922
+      produces:
923
+      - application/json
924
+      responses:
925
+        "200":
926
+          description: OK
927
+          schema:
928
+            allOf:
929
+            - $ref: '#/definitions/response.Response'
930
+            - properties:
931
+                data:
932
+                  $ref: '#/definitions/model.LoginLog'
933
+              type: object
934
+        "500":
935
+          description: Internal Server Error
936
+          schema:
937
+            $ref: '#/definitions/response.Response'
938
+      security:
939
+      - token: []
940
+      summary: 登录日志详情
941
+      tags:
942
+      - 登录日志
943
+  /admin/loginLog/list:
944
+    get:
945
+      consumes:
946
+      - application/json
947
+      description: 登录日志列表
948
+      parameters:
949
+      - description: 页码
950
+        in: query
951
+        name: page
952
+        type: integer
953
+      - description: 页大小
954
+        in: query
955
+        name: page_size
956
+        type: integer
957
+      - description: 用户ID
958
+        in: query
959
+        name: user_id
960
+        type: integer
961
+      produces:
962
+      - application/json
963
+      responses:
964
+        "200":
965
+          description: OK
966
+          schema:
967
+            allOf:
968
+            - $ref: '#/definitions/response.Response'
969
+            - properties:
970
+                data:
971
+                  $ref: '#/definitions/model.LoginLogList'
972
+              type: object
973
+        "500":
974
+          description: Internal Server Error
975
+          schema:
976
+            $ref: '#/definitions/response.Response'
977
+      security:
978
+      - token: []
979
+      summary: 登录日志列表
980
+      tags:
981
+      - 登录日志
785 982
   /admin/logout:
786 983
     post:
787 984
       consumes:
@@ -801,6 +998,167 @@ paths:
801 998
       summary: 登出
802 999
       tags:
803 1000
       - 登录
1001
+  /admin/oauth/create:
1002
+    post:
1003
+      consumes:
1004
+      - application/json
1005
+      description: 创建Oauth
1006
+      parameters:
1007
+      - description: Oauth信息
1008
+        in: body
1009
+        name: body
1010
+        required: true
1011
+        schema:
1012
+          $ref: '#/definitions/admin.OauthForm'
1013
+      produces:
1014
+      - application/json
1015
+      responses:
1016
+        "200":
1017
+          description: OK
1018
+          schema:
1019
+            allOf:
1020
+            - $ref: '#/definitions/response.Response'
1021
+            - properties:
1022
+                data:
1023
+                  $ref: '#/definitions/model.Oauth'
1024
+              type: object
1025
+        "500":
1026
+          description: Internal Server Error
1027
+          schema:
1028
+            $ref: '#/definitions/response.Response'
1029
+      security:
1030
+      - token: []
1031
+      summary: 创建Oauth
1032
+      tags:
1033
+      - Oauth
1034
+  /admin/oauth/delete:
1035
+    post:
1036
+      consumes:
1037
+      - application/json
1038
+      description: Oauth删除
1039
+      parameters:
1040
+      - description: Oauth信息
1041
+        in: body
1042
+        name: body
1043
+        required: true
1044
+        schema:
1045
+          $ref: '#/definitions/admin.OauthForm'
1046
+      produces:
1047
+      - application/json
1048
+      responses:
1049
+        "200":
1050
+          description: OK
1051
+          schema:
1052
+            $ref: '#/definitions/response.Response'
1053
+        "500":
1054
+          description: Internal Server Error
1055
+          schema:
1056
+            $ref: '#/definitions/response.Response'
1057
+      security:
1058
+      - token: []
1059
+      summary: Oauth删除
1060
+      tags:
1061
+      - Oauth
1062
+  /admin/oauth/detail/{id}:
1063
+    get:
1064
+      consumes:
1065
+      - application/json
1066
+      description: Oauth详情
1067
+      parameters:
1068
+      - description: ID
1069
+        in: path
1070
+        name: id
1071
+        required: true
1072
+        type: integer
1073
+      produces:
1074
+      - application/json
1075
+      responses:
1076
+        "200":
1077
+          description: OK
1078
+          schema:
1079
+            allOf:
1080
+            - $ref: '#/definitions/response.Response'
1081
+            - properties:
1082
+                data:
1083
+                  $ref: '#/definitions/model.Oauth'
1084
+              type: object
1085
+        "500":
1086
+          description: Internal Server Error
1087
+          schema:
1088
+            $ref: '#/definitions/response.Response'
1089
+      security:
1090
+      - token: []
1091
+      summary: Oauth详情
1092
+      tags:
1093
+      - Oauth
1094
+  /admin/oauth/list:
1095
+    get:
1096
+      consumes:
1097
+      - application/json
1098
+      description: Oauth列表
1099
+      parameters:
1100
+      - description: 页码
1101
+        in: query
1102
+        name: page
1103
+        type: integer
1104
+      - description: 页大小
1105
+        in: query
1106
+        name: page_size
1107
+        type: integer
1108
+      produces:
1109
+      - application/json
1110
+      responses:
1111
+        "200":
1112
+          description: OK
1113
+          schema:
1114
+            allOf:
1115
+            - $ref: '#/definitions/response.Response'
1116
+            - properties:
1117
+                data:
1118
+                  $ref: '#/definitions/model.OauthList'
1119
+              type: object
1120
+        "500":
1121
+          description: Internal Server Error
1122
+          schema:
1123
+            $ref: '#/definitions/response.Response'
1124
+      security:
1125
+      - token: []
1126
+      summary: Oauth列表
1127
+      tags:
1128
+      - Oauth
1129
+  /admin/oauth/update:
1130
+    post:
1131
+      consumes:
1132
+      - application/json
1133
+      description: Oauth编辑
1134
+      parameters:
1135
+      - description: Oauth信息
1136
+        in: body
1137
+        name: body
1138
+        required: true
1139
+        schema:
1140
+          $ref: '#/definitions/admin.OauthForm'
1141
+      produces:
1142
+      - application/json
1143
+      responses:
1144
+        "200":
1145
+          description: OK
1146
+          schema:
1147
+            allOf:
1148
+            - $ref: '#/definitions/response.Response'
1149
+            - properties:
1150
+                data:
1151
+                  $ref: '#/definitions/model.OauthList'
1152
+              type: object
1153
+        "500":
1154
+          description: Internal Server Error
1155
+          schema:
1156
+            $ref: '#/definitions/response.Response'
1157
+      security:
1158
+      - token: []
1159
+      summary: Oauth编辑
1160
+      tags:
1161
+      - Oauth
804 1162
   /admin/peer/create:
805 1163
     post:
806 1164
       consumes:
@@ -1338,6 +1696,34 @@ paths:
1338 1696
       summary: 管理员列表
1339 1697
       tags:
1340 1698
       - 用户
1699
+  /admin/user/myOauth:
1700
+    get:
1701
+      consumes:
1702
+      - application/json
1703
+      description: 我的授权
1704
+      produces:
1705
+      - application/json
1706
+      responses:
1707
+        "200":
1708
+          description: OK
1709
+          schema:
1710
+            allOf:
1711
+            - $ref: '#/definitions/response.Response'
1712
+            - properties:
1713
+                data:
1714
+                  items:
1715
+                    $ref: '#/definitions/admin.UserOauthItem'
1716
+                  type: array
1717
+              type: object
1718
+        "500":
1719
+          description: Internal Server Error
1720
+          schema:
1721
+            $ref: '#/definitions/response.Response'
1722
+      security:
1723
+      - token: []
1724
+      summary: 我的授权
1725
+      tags:
1726
+      - 用户
1341 1727
   /admin/user/update:
1342 1728
     post:
1343 1729
       consumes:

BIN
docs/admin_webclient.png


+ 149 - 34
docs/api/api_docs.go

@@ -233,40 +233,6 @@ const docTemplateapi = `{
233 233
                 }
234 234
             }
235 235
         },
236
-        "/currentUser": {
237
-            "get": {
238
-                "security": [
239
-                    {
240
-                        "token": []
241
-                    }
242
-                ],
243
-                "description": "用户信息",
244
-                "consumes": [
245
-                    "application/json"
246
-                ],
247
-                "produces": [
248
-                    "application/json"
249
-                ],
250
-                "tags": [
251
-                    "用户"
252
-                ],
253
-                "summary": "用户信息",
254
-                "responses": {
255
-                    "200": {
256
-                        "description": "OK",
257
-                        "schema": {
258
-                            "$ref": "#/definitions/api.UserPayload"
259
-                        }
260
-                    },
261
-                    "500": {
262
-                        "description": "Internal Server Error",
263
-                        "schema": {
264
-                            "$ref": "#/definitions/response.Response"
265
-                        }
266
-                    }
267
-                }
268
-            }
269
-        },
270 236
         "/heartbeat": {
271 237
             "post": {
272 238
                 "description": "心跳",
@@ -394,6 +360,122 @@ const docTemplateapi = `{
394 360
                 }
395 361
             }
396 362
         },
363
+        "/oauth/callback": {
364
+            "get": {
365
+                "description": "OauthCallback",
366
+                "consumes": [
367
+                    "application/json"
368
+                ],
369
+                "produces": [
370
+                    "application/json"
371
+                ],
372
+                "tags": [
373
+                    "Oauth"
374
+                ],
375
+                "summary": "OauthCallback",
376
+                "responses": {
377
+                    "200": {
378
+                        "description": "OK",
379
+                        "schema": {
380
+                            "$ref": "#/definitions/api.LoginRes"
381
+                        }
382
+                    },
383
+                    "500": {
384
+                        "description": "Internal Server Error",
385
+                        "schema": {
386
+                            "$ref": "#/definitions/response.ErrorResponse"
387
+                        }
388
+                    }
389
+                }
390
+            }
391
+        },
392
+        "/oauth/login": {
393
+            "get": {
394
+                "description": "WebOauthLogin",
395
+                "consumes": [
396
+                    "application/json"
397
+                ],
398
+                "produces": [
399
+                    "application/json"
400
+                ],
401
+                "tags": [
402
+                    "Oauth"
403
+                ],
404
+                "summary": "WebOauthLogin",
405
+                "responses": {
406
+                    "200": {
407
+                        "description": "OK",
408
+                        "schema": {
409
+                            "type": "string"
410
+                        }
411
+                    },
412
+                    "500": {
413
+                        "description": "Internal Server Error",
414
+                        "schema": {
415
+                            "type": "string"
416
+                        }
417
+                    }
418
+                }
419
+            }
420
+        },
421
+        "/oidc/auth": {
422
+            "post": {
423
+                "description": "OidcAuth",
424
+                "consumes": [
425
+                    "application/json"
426
+                ],
427
+                "produces": [
428
+                    "application/json"
429
+                ],
430
+                "tags": [
431
+                    "Oauth"
432
+                ],
433
+                "summary": "OidcAuth",
434
+                "responses": {
435
+                    "200": {
436
+                        "description": "OK",
437
+                        "schema": {
438
+                            "$ref": "#/definitions/api.LoginRes"
439
+                        }
440
+                    },
441
+                    "500": {
442
+                        "description": "Internal Server Error",
443
+                        "schema": {
444
+                            "$ref": "#/definitions/response.ErrorResponse"
445
+                        }
446
+                    }
447
+                }
448
+            }
449
+        },
450
+        "/oidc/auth-query": {
451
+            "get": {
452
+                "description": "OidcAuthQuery",
453
+                "consumes": [
454
+                    "application/json"
455
+                ],
456
+                "produces": [
457
+                    "application/json"
458
+                ],
459
+                "tags": [
460
+                    "Oauth"
461
+                ],
462
+                "summary": "OidcAuthQuery",
463
+                "responses": {
464
+                    "200": {
465
+                        "description": "OK",
466
+                        "schema": {
467
+                            "$ref": "#/definitions/api.LoginRes"
468
+                        }
469
+                    },
470
+                    "500": {
471
+                        "description": "Internal Server Error",
472
+                        "schema": {
473
+                            "$ref": "#/definitions/response.ErrorResponse"
474
+                        }
475
+                    }
476
+                }
477
+            }
478
+        },
397 479
         "/peers": {
398 480
             "get": {
399 481
                 "security": [
@@ -656,21 +738,50 @@ const docTemplateapi = `{
656 738
                 }
657 739
             }
658 740
         },
741
+        "api.DeviceInfoInLogin": {
742
+            "type": "object",
743
+            "properties": {
744
+                "name": {
745
+                    "type": "string"
746
+                },
747
+                "os": {
748
+                    "type": "string"
749
+                },
750
+                "type": {
751
+                    "type": "string"
752
+                }
753
+            }
754
+        },
659 755
         "api.LoginForm": {
660 756
             "type": "object",
661 757
             "required": [
662 758
                 "username"
663 759
             ],
664 760
             "properties": {
761
+                "autoLogin": {
762
+                    "type": "boolean"
763
+                },
764
+                "deviceInfo": {
765
+                    "$ref": "#/definitions/api.DeviceInfoInLogin"
766
+                },
767
+                "id": {
768
+                    "type": "string"
769
+                },
665 770
                 "password": {
666 771
                     "type": "string",
667 772
                     "maxLength": 20,
668 773
                     "minLength": 4
669 774
                 },
775
+                "type": {
776
+                    "type": "string"
777
+                },
670 778
                 "username": {
671 779
                     "type": "string",
672 780
                     "maxLength": 10,
673 781
                     "minLength": 4
782
+                },
783
+                "uuid": {
784
+                    "type": "string"
674 785
                 }
675 786
             }
676 787
         },
@@ -729,6 +840,10 @@ const docTemplateapi = `{
729 840
                 "email": {
730 841
                     "type": "string"
731 842
                 },
843
+                "info": {
844
+                    "type": "object",
845
+                    "additionalProperties": true
846
+                },
732 847
                 "is_admin": {
733 848
                     "type": "boolean"
734 849
                 },

+ 149 - 34
docs/api/api_swagger.json

@@ -226,40 +226,6 @@
226 226
                 }
227 227
             }
228 228
         },
229
-        "/currentUser": {
230
-            "get": {
231
-                "security": [
232
-                    {
233
-                        "token": []
234
-                    }
235
-                ],
236
-                "description": "用户信息",
237
-                "consumes": [
238
-                    "application/json"
239
-                ],
240
-                "produces": [
241
-                    "application/json"
242
-                ],
243
-                "tags": [
244
-                    "用户"
245
-                ],
246
-                "summary": "用户信息",
247
-                "responses": {
248
-                    "200": {
249
-                        "description": "OK",
250
-                        "schema": {
251
-                            "$ref": "#/definitions/api.UserPayload"
252
-                        }
253
-                    },
254
-                    "500": {
255
-                        "description": "Internal Server Error",
256
-                        "schema": {
257
-                            "$ref": "#/definitions/response.Response"
258
-                        }
259
-                    }
260
-                }
261
-            }
262
-        },
263 229
         "/heartbeat": {
264 230
             "post": {
265 231
                 "description": "心跳",
@@ -387,6 +353,122 @@
387 353
                 }
388 354
             }
389 355
         },
356
+        "/oauth/callback": {
357
+            "get": {
358
+                "description": "OauthCallback",
359
+                "consumes": [
360
+                    "application/json"
361
+                ],
362
+                "produces": [
363
+                    "application/json"
364
+                ],
365
+                "tags": [
366
+                    "Oauth"
367
+                ],
368
+                "summary": "OauthCallback",
369
+                "responses": {
370
+                    "200": {
371
+                        "description": "OK",
372
+                        "schema": {
373
+                            "$ref": "#/definitions/api.LoginRes"
374
+                        }
375
+                    },
376
+                    "500": {
377
+                        "description": "Internal Server Error",
378
+                        "schema": {
379
+                            "$ref": "#/definitions/response.ErrorResponse"
380
+                        }
381
+                    }
382
+                }
383
+            }
384
+        },
385
+        "/oauth/login": {
386
+            "get": {
387
+                "description": "WebOauthLogin",
388
+                "consumes": [
389
+                    "application/json"
390
+                ],
391
+                "produces": [
392
+                    "application/json"
393
+                ],
394
+                "tags": [
395
+                    "Oauth"
396
+                ],
397
+                "summary": "WebOauthLogin",
398
+                "responses": {
399
+                    "200": {
400
+                        "description": "OK",
401
+                        "schema": {
402
+                            "type": "string"
403
+                        }
404
+                    },
405
+                    "500": {
406
+                        "description": "Internal Server Error",
407
+                        "schema": {
408
+                            "type": "string"
409
+                        }
410
+                    }
411
+                }
412
+            }
413
+        },
414
+        "/oidc/auth": {
415
+            "post": {
416
+                "description": "OidcAuth",
417
+                "consumes": [
418
+                    "application/json"
419
+                ],
420
+                "produces": [
421
+                    "application/json"
422
+                ],
423
+                "tags": [
424
+                    "Oauth"
425
+                ],
426
+                "summary": "OidcAuth",
427
+                "responses": {
428
+                    "200": {
429
+                        "description": "OK",
430
+                        "schema": {
431
+                            "$ref": "#/definitions/api.LoginRes"
432
+                        }
433
+                    },
434
+                    "500": {
435
+                        "description": "Internal Server Error",
436
+                        "schema": {
437
+                            "$ref": "#/definitions/response.ErrorResponse"
438
+                        }
439
+                    }
440
+                }
441
+            }
442
+        },
443
+        "/oidc/auth-query": {
444
+            "get": {
445
+                "description": "OidcAuthQuery",
446
+                "consumes": [
447
+                    "application/json"
448
+                ],
449
+                "produces": [
450
+                    "application/json"
451
+                ],
452
+                "tags": [
453
+                    "Oauth"
454
+                ],
455
+                "summary": "OidcAuthQuery",
456
+                "responses": {
457
+                    "200": {
458
+                        "description": "OK",
459
+                        "schema": {
460
+                            "$ref": "#/definitions/api.LoginRes"
461
+                        }
462
+                    },
463
+                    "500": {
464
+                        "description": "Internal Server Error",
465
+                        "schema": {
466
+                            "$ref": "#/definitions/response.ErrorResponse"
467
+                        }
468
+                    }
469
+                }
470
+            }
471
+        },
390 472
         "/peers": {
391 473
             "get": {
392 474
                 "security": [
@@ -649,21 +731,50 @@
649 731
                 }
650 732
             }
651 733
         },
734
+        "api.DeviceInfoInLogin": {
735
+            "type": "object",
736
+            "properties": {
737
+                "name": {
738
+                    "type": "string"
739
+                },
740
+                "os": {
741
+                    "type": "string"
742
+                },
743
+                "type": {
744
+                    "type": "string"
745
+                }
746
+            }
747
+        },
652 748
         "api.LoginForm": {
653 749
             "type": "object",
654 750
             "required": [
655 751
                 "username"
656 752
             ],
657 753
             "properties": {
754
+                "autoLogin": {
755
+                    "type": "boolean"
756
+                },
757
+                "deviceInfo": {
758
+                    "$ref": "#/definitions/api.DeviceInfoInLogin"
759
+                },
760
+                "id": {
761
+                    "type": "string"
762
+                },
658 763
                 "password": {
659 764
                     "type": "string",
660 765
                     "maxLength": 20,
661 766
                     "minLength": 4
662 767
                 },
768
+                "type": {
769
+                    "type": "string"
770
+                },
663 771
                 "username": {
664 772
                     "type": "string",
665 773
                     "maxLength": 10,
666 774
                     "minLength": 4
775
+                },
776
+                "uuid": {
777
+                    "type": "string"
667 778
                 }
668 779
             }
669 780
         },
@@ -722,6 +833,10 @@
722 833
                 "email": {
723 834
                     "type": "string"
724 835
                 },
836
+                "info": {
837
+                    "type": "object",
838
+                    "additionalProperties": true
839
+                },
725 840
                 "is_admin": {
726 841
                     "type": "boolean"
727 842
                 },

+ 98 - 21
docs/api/api_swagger.yaml

@@ -6,16 +6,35 @@ definitions:
6 6
         example: '{"tags":["tag1","tag2","tag3"],"peers":[{"id":"abc","username":"abv-l","hostname":"","platform":"Windows","alias":"","tags":["tag1","tag2"],"hash":"hash"}],"tag_colors":"{\"tag1\":4288585374,\"tag2\":4278238420,\"tag3\":4291681337}"}'
7 7
         type: string
8 8
     type: object
9
+  api.DeviceInfoInLogin:
10
+    properties:
11
+      name:
12
+        type: string
13
+      os:
14
+        type: string
15
+      type:
16
+        type: string
17
+    type: object
9 18
   api.LoginForm:
10 19
     properties:
20
+      autoLogin:
21
+        type: boolean
22
+      deviceInfo:
23
+        $ref: '#/definitions/api.DeviceInfoInLogin'
24
+      id:
25
+        type: string
11 26
       password:
12 27
         maxLength: 20
13 28
         minLength: 4
14 29
         type: string
30
+      type:
31
+        type: string
15 32
       username:
16 33
         maxLength: 10
17 34
         minLength: 4
18 35
         type: string
36
+      uuid:
37
+        type: string
19 38
     required:
20 39
     - username
21 40
     type: object
@@ -55,6 +74,9 @@ definitions:
55 74
     properties:
56 75
       email:
57 76
         type: string
77
+      info:
78
+        additionalProperties: true
79
+        type: object
58 80
       is_admin:
59 81
         type: boolean
60 82
       name:
@@ -242,27 +264,6 @@ paths:
242 264
       summary: 用户信息
243 265
       tags:
244 266
       - 用户
245
-  /currentUser:
246
-    get:
247
-      consumes:
248
-      - application/json
249
-      description: 用户信息
250
-      produces:
251
-      - application/json
252
-      responses:
253
-        "200":
254
-          description: OK
255
-          schema:
256
-            $ref: '#/definitions/api.UserPayload'
257
-        "500":
258
-          description: Internal Server Error
259
-          schema:
260
-            $ref: '#/definitions/response.Response'
261
-      security:
262
-      - token: []
263
-      summary: 用户信息
264
-      tags:
265
-      - 用户
266 267
   /heartbeat:
267 268
     post:
268 269
       consumes:
@@ -346,6 +347,82 @@ paths:
346 347
       summary: 登出
347 348
       tags:
348 349
       - 登录
350
+  /oauth/callback:
351
+    get:
352
+      consumes:
353
+      - application/json
354
+      description: OauthCallback
355
+      produces:
356
+      - application/json
357
+      responses:
358
+        "200":
359
+          description: OK
360
+          schema:
361
+            $ref: '#/definitions/api.LoginRes'
362
+        "500":
363
+          description: Internal Server Error
364
+          schema:
365
+            $ref: '#/definitions/response.ErrorResponse'
366
+      summary: OauthCallback
367
+      tags:
368
+      - Oauth
369
+  /oauth/login:
370
+    get:
371
+      consumes:
372
+      - application/json
373
+      description: WebOauthLogin
374
+      produces:
375
+      - application/json
376
+      responses:
377
+        "200":
378
+          description: OK
379
+          schema:
380
+            type: string
381
+        "500":
382
+          description: Internal Server Error
383
+          schema:
384
+            type: string
385
+      summary: WebOauthLogin
386
+      tags:
387
+      - Oauth
388
+  /oidc/auth:
389
+    post:
390
+      consumes:
391
+      - application/json
392
+      description: OidcAuth
393
+      produces:
394
+      - application/json
395
+      responses:
396
+        "200":
397
+          description: OK
398
+          schema:
399
+            $ref: '#/definitions/api.LoginRes'
400
+        "500":
401
+          description: Internal Server Error
402
+          schema:
403
+            $ref: '#/definitions/response.ErrorResponse'
404
+      summary: OidcAuth
405
+      tags:
406
+      - Oauth
407
+  /oidc/auth-query:
408
+    get:
409
+      consumes:
410
+      - application/json
411
+      description: OidcAuthQuery
412
+      produces:
413
+      - application/json
414
+      responses:
415
+        "200":
416
+          description: OK
417
+          schema:
418
+            $ref: '#/definitions/api.LoginRes'
419
+        "500":
420
+          description: Internal Server Error
421
+          schema:
422
+            $ref: '#/definitions/response.ErrorResponse'
423
+      summary: OidcAuthQuery
424
+      tags:
425
+      - Oauth
349 426
   /peers:
350 427
     get:
351 428
       consumes:

BIN
docs/pc_login.png


BIN
docs/web_admin.png


BIN
docs/web_admin_user.png


BIN
docs/web_user.png


BIN
docs/webclient_conf.png


+ 3 - 0
generate_run.go

@@ -0,0 +1,3 @@
1
+package Gwen
2
+
3
+//go:generate go run cmd/apimain.go

+ 7 - 5
go.mod

@@ -23,6 +23,7 @@ require (
23 23
 )
24 24
 
25 25
 require (
26
+	cloud.google.com/go/compute/metadata v0.5.1 // indirect
26 27
 	github.com/KyleBanks/depth v1.2.1 // indirect
27 28
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
28 29
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
@@ -61,12 +62,13 @@ require (
61 62
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
62 63
 	github.com/ugorji/go/codec v1.2.9 // indirect
63 64
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
64
-	golang.org/x/crypto v0.14.0 // indirect
65
-	golang.org/x/net v0.17.0 // indirect
66
-	golang.org/x/sys v0.13.0 // indirect
67
-	golang.org/x/text v0.13.0 // indirect
65
+	golang.org/x/crypto v0.23.0 // indirect
66
+	golang.org/x/net v0.21.0 // indirect
67
+	golang.org/x/oauth2 v0.23.0 // indirect
68
+	golang.org/x/sys v0.25.0 // indirect
69
+	golang.org/x/text v0.15.0 // indirect
68 70
 	golang.org/x/tools v0.7.0 // indirect
69
-	google.golang.org/protobuf v1.28.1 // indirect
71
+	google.golang.org/protobuf v1.33.0 // indirect
70 72
 	gopkg.in/ini.v1 v1.63.2 // indirect
71 73
 	gopkg.in/yaml.v2 v2.4.0 // indirect
72 74
 	gopkg.in/yaml.v3 v3.0.1 // indirect

+ 9 - 1
http/controller/admin/login.go

@@ -5,6 +5,7 @@ import (
5 5
 	"Gwen/http/request/admin"
6 6
 	"Gwen/http/response"
7 7
 	adResp "Gwen/http/response/admin"
8
+	"Gwen/model"
8 9
 	"Gwen/service"
9 10
 	"github.com/gin-gonic/gin"
10 11
 )
@@ -43,7 +44,14 @@ func (ct *Login) Login(c *gin.Context) {
43 44
 		return
44 45
 	}
45 46
 
46
-	ut := service.AllService.UserService.Login(u)
47
+	ut := service.AllService.UserService.Login(u, &model.LoginLog{
48
+		UserId:   u.Id,
49
+		Client:   "webadmin",
50
+		Uuid:     "",
51
+		Ip:       c.ClientIP(),
52
+		Type:     "account",
53
+		Platform: f.Platform,
54
+	})
47 55
 
48 56
 	response.Success(c, &adResp.LoginPayload{
49 57
 		Token:      ut.Token,

+ 110 - 0
http/controller/admin/loginLog.go

@@ -0,0 +1,110 @@
1
+package admin
2
+
3
+import (
4
+	"Gwen/global"
5
+	"Gwen/http/request/admin"
6
+	"Gwen/http/response"
7
+	"Gwen/model"
8
+	"Gwen/service"
9
+	"github.com/gin-gonic/gin"
10
+	"gorm.io/gorm"
11
+	"strconv"
12
+)
13
+
14
+type LoginLog struct {
15
+}
16
+
17
+// Detail 登录日志
18
+// @Tags 登录日志
19
+// @Summary 登录日志详情
20
+// @Description 登录日志详情
21
+// @Accept  json
22
+// @Produce  json
23
+// @Param id path int true "ID"
24
+// @Success 200 {object} response.Response{data=model.LoginLog}
25
+// @Failure 500 {object} response.Response
26
+// @Router /admin/loginLog/detail/{id} [get]
27
+// @Security token
28
+func (ct *LoginLog) Detail(c *gin.Context) {
29
+	id := c.Param("id")
30
+	iid, _ := strconv.Atoi(id)
31
+	u := service.AllService.LoginLogService.InfoById(uint(iid))
32
+	if u.Id > 0 {
33
+		response.Success(c, u)
34
+		return
35
+	}
36
+	response.Fail(c, 101, "信息不存在")
37
+	return
38
+}
39
+
40
+// List 列表
41
+// @Tags 登录日志
42
+// @Summary 登录日志列表
43
+// @Description 登录日志列表
44
+// @Accept  json
45
+// @Produce  json
46
+// @Param page query int false "页码"
47
+// @Param page_size query int false "页大小"
48
+// @Param user_id query int false "用户ID"
49
+// @Success 200 {object} response.Response{data=model.LoginLogList}
50
+// @Failure 500 {object} response.Response
51
+// @Router /admin/loginLog/list [get]
52
+// @Security token
53
+func (ct *LoginLog) List(c *gin.Context) {
54
+	query := &admin.LoginLogQuery{}
55
+	if err := c.ShouldBindQuery(query); err != nil {
56
+		response.Fail(c, 101, "参数错误")
57
+		return
58
+	}
59
+	u := service.AllService.UserService.CurUser(c)
60
+	if !service.AllService.UserService.IsAdmin(u) || query.IsMy == 1 {
61
+		query.UserId = int(u.Id)
62
+	}
63
+	res := service.AllService.LoginLogService.List(query.Page, query.PageSize, func(tx *gorm.DB) {
64
+		if query.UserId > 0 {
65
+			tx.Where("user_id = ?", query.UserId)
66
+		}
67
+	})
68
+	response.Success(c, res)
69
+}
70
+
71
+// Delete 删除
72
+// @Tags 登录日志
73
+// @Summary 登录日志删除
74
+// @Description 登录日志删除
75
+// @Accept  json
76
+// @Produce  json
77
+// @Param body body model.LoginLog true "登录日志信息"
78
+// @Success 200 {object} response.Response
79
+// @Failure 500 {object} response.Response
80
+// @Router /admin/loginLog/delete [post]
81
+// @Security token
82
+func (ct *LoginLog) Delete(c *gin.Context) {
83
+	f := &model.LoginLog{}
84
+	if err := c.ShouldBindJSON(f); err != nil {
85
+		response.Fail(c, 101, "系统错误")
86
+		return
87
+	}
88
+	id := f.Id
89
+	errList := global.Validator.ValidVar(id, "required,gt=0")
90
+	if len(errList) > 0 {
91
+		response.Fail(c, 101, errList[0])
92
+		return
93
+	}
94
+	l := service.AllService.LoginLogService.InfoById(f.Id)
95
+	u := service.AllService.UserService.CurUser(c)
96
+	if !service.AllService.UserService.IsAdmin(u) && l.UserId != u.Id {
97
+		response.Fail(c, 101, "无权限")
98
+		return
99
+	}
100
+	if l.Id > 0 {
101
+		err := service.AllService.LoginLogService.Delete(l)
102
+		if err == nil {
103
+			response.Success(c, nil)
104
+			return
105
+		}
106
+		response.Fail(c, 101, err.Error())
107
+		return
108
+	}
109
+	response.Fail(c, 101, "信息不存在")
110
+}

+ 291 - 0
http/controller/admin/oauth.go

@@ -0,0 +1,291 @@
1
+package admin
2
+
3
+import (
4
+	"Gwen/global"
5
+	"Gwen/http/request/admin"
6
+	adminReq "Gwen/http/request/admin"
7
+	"Gwen/http/response"
8
+	"Gwen/model"
9
+	"Gwen/service"
10
+	"github.com/gin-gonic/gin"
11
+	"strconv"
12
+)
13
+
14
+type Oauth struct {
15
+}
16
+
17
+// Info
18
+func (o *Oauth) Info(c *gin.Context) {
19
+	code := c.Query("code")
20
+	if code == "" {
21
+		response.Fail(c, 101, "参数错误")
22
+		return
23
+	}
24
+	v := service.AllService.OauthService.GetOauthCache(code)
25
+	if v == nil {
26
+		response.Fail(c, 101, "信息不存在")
27
+		return
28
+	}
29
+	response.Success(c, v)
30
+}
31
+
32
+func (o *Oauth) ToBind(c *gin.Context) {
33
+	f := &adminReq.BindOauthForm{}
34
+	err := c.ShouldBindJSON(f)
35
+	if err != nil {
36
+		response.Fail(c, 101, "参数错误")
37
+		return
38
+	}
39
+	u := service.AllService.UserService.CurUser(c)
40
+
41
+	utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
42
+	if utr.Id > 0 {
43
+		response.Fail(c, 101, "已绑定过了")
44
+		return
45
+	}
46
+
47
+	err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
48
+	if err != nil {
49
+		response.Error(c, err.Error())
50
+		return
51
+	}
52
+
53
+	service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
54
+		Action: service.OauthActionTypeBind,
55
+		Op:     f.Op,
56
+		UserId: u.Id,
57
+	}, 5*60)
58
+
59
+	response.Success(c, gin.H{
60
+		"code": code,
61
+		"url":  url,
62
+	})
63
+}
64
+
65
+// Confirm 确认授权登录
66
+func (o *Oauth) Confirm(c *gin.Context) {
67
+	j := &adminReq.OauthConfirmForm{}
68
+	err := c.ShouldBindJSON(j)
69
+	if err != nil {
70
+		response.Fail(c, 101, "参数错误"+err.Error())
71
+		return
72
+	}
73
+	if j.Code == "" {
74
+		response.Fail(c, 101, "参数错误: code 不存在")
75
+		return
76
+	}
77
+	v := service.AllService.OauthService.GetOauthCache(j.Code)
78
+	if v == nil {
79
+		response.Fail(c, 101, "授权已过期")
80
+		return
81
+	}
82
+	u := service.AllService.UserService.CurUser(c)
83
+	v.UserId = u.Id
84
+	service.AllService.OauthService.SetOauthCache(j.Code, v, 0)
85
+	response.Success(c, v)
86
+}
87
+
88
+func (o *Oauth) BindConfirm(c *gin.Context) {
89
+	j := &adminReq.OauthConfirmForm{}
90
+	err := c.ShouldBindJSON(j)
91
+	if err != nil {
92
+		response.Fail(c, 101, "参数错误"+err.Error())
93
+		return
94
+	}
95
+	if j.Code == "" {
96
+		response.Fail(c, 101, "参数错误: code 不存在")
97
+		return
98
+	}
99
+	v := service.AllService.OauthService.GetOauthCache(j.Code)
100
+	if v == nil {
101
+		response.Fail(c, 101, "授权已过期")
102
+		return
103
+	}
104
+	u := service.AllService.UserService.CurUser(c)
105
+	err = service.AllService.OauthService.BindGithubUser(v.ThirdOpenId, v.ThirdOpenId, u.Id)
106
+	if err != nil {
107
+		response.Fail(c, 101, "绑定失败,请重试")
108
+		return
109
+	}
110
+
111
+	v.UserId = u.Id
112
+	service.AllService.OauthService.SetOauthCache(j.Code, v, 0)
113
+	response.Success(c, v)
114
+}
115
+
116
+func (o *Oauth) Unbind(c *gin.Context) {
117
+	f := &adminReq.UnBindOauthForm{}
118
+	err := c.ShouldBindJSON(f)
119
+	if err != nil {
120
+		response.Fail(c, 101, "参数错误")
121
+		return
122
+	}
123
+	u := service.AllService.UserService.CurUser(c)
124
+	utr := service.AllService.UserService.UserThirdInfo(u.Id, f.Op)
125
+	if utr.Id == 0 {
126
+		response.Fail(c, 101, "未绑定")
127
+		return
128
+	}
129
+	if f.Op == model.OauthTypeGithub {
130
+		err = service.AllService.OauthService.UnBindGithubUser(u.Id)
131
+		if err != nil {
132
+			response.Fail(c, 101, "解绑失败")
133
+			return
134
+		}
135
+	}
136
+	response.Success(c, nil)
137
+}
138
+
139
+// Detail Oauth
140
+// @Tags Oauth
141
+// @Summary Oauth详情
142
+// @Description Oauth详情
143
+// @Accept  json
144
+// @Produce  json
145
+// @Param id path int true "ID"
146
+// @Success 200 {object} response.Response{data=model.Oauth}
147
+// @Failure 500 {object} response.Response
148
+// @Router /admin/oauth/detail/{id} [get]
149
+// @Security token
150
+func (o *Oauth) Detail(c *gin.Context) {
151
+	id := c.Param("id")
152
+	iid, _ := strconv.Atoi(id)
153
+	u := service.AllService.OauthService.InfoById(uint(iid))
154
+	if u.Id > 0 {
155
+		response.Success(c, u)
156
+		return
157
+	}
158
+	response.Fail(c, 101, "信息不存在")
159
+	return
160
+}
161
+
162
+// Create 创建Oauth
163
+// @Tags Oauth
164
+// @Summary 创建Oauth
165
+// @Description 创建Oauth
166
+// @Accept  json
167
+// @Produce  json
168
+// @Param body body admin.OauthForm true "Oauth信息"
169
+// @Success 200 {object} response.Response{data=model.Oauth}
170
+// @Failure 500 {object} response.Response
171
+// @Router /admin/oauth/create [post]
172
+// @Security token
173
+func (o *Oauth) Create(c *gin.Context) {
174
+	f := &admin.OauthForm{}
175
+	if err := c.ShouldBindJSON(f); err != nil {
176
+		response.Fail(c, 101, "参数错误"+err.Error())
177
+		return
178
+	}
179
+	errList := global.Validator.ValidStruct(f)
180
+	if len(errList) > 0 {
181
+		response.Fail(c, 101, errList[0])
182
+		return
183
+	}
184
+
185
+	ex := service.AllService.OauthService.InfoByOp(f.Op)
186
+	if ex.Id > 0 {
187
+		response.Fail(c, 101, "已存在"+f.Op)
188
+		return
189
+	}
190
+
191
+	u := f.ToOauth()
192
+	err := service.AllService.OauthService.Create(u)
193
+	if err != nil {
194
+		response.Fail(c, 101, "创建失败")
195
+		return
196
+	}
197
+	response.Success(c, u)
198
+}
199
+
200
+// List 列表
201
+// @Tags Oauth
202
+// @Summary Oauth列表
203
+// @Description Oauth列表
204
+// @Accept  json
205
+// @Produce  json
206
+// @Param page query int false "页码"
207
+// @Param page_size query int false "页大小"
208
+// @Success 200 {object} response.Response{data=model.OauthList}
209
+// @Failure 500 {object} response.Response
210
+// @Router /admin/oauth/list [get]
211
+// @Security token
212
+func (o *Oauth) List(c *gin.Context) {
213
+	query := &admin.PageQuery{}
214
+	if err := c.ShouldBindQuery(query); err != nil {
215
+		response.Fail(c, 101, "参数错误")
216
+		return
217
+	}
218
+	res := service.AllService.OauthService.List(query.Page, query.PageSize, nil)
219
+	response.Success(c, res)
220
+}
221
+
222
+// Update 编辑
223
+// @Tags Oauth
224
+// @Summary Oauth编辑
225
+// @Description Oauth编辑
226
+// @Accept  json
227
+// @Produce  json
228
+// @Param body body admin.OauthForm true "Oauth信息"
229
+// @Success 200 {object} response.Response{data=model.OauthList}
230
+// @Failure 500 {object} response.Response
231
+// @Router /admin/oauth/update [post]
232
+// @Security token
233
+func (o *Oauth) Update(c *gin.Context) {
234
+	f := &admin.OauthForm{}
235
+	if err := c.ShouldBindJSON(f); err != nil {
236
+		response.Fail(c, 101, "参数错误")
237
+		return
238
+	}
239
+	if f.Id == 0 {
240
+		response.Fail(c, 101, "参数错误")
241
+		return
242
+	}
243
+	errList := global.Validator.ValidStruct(f)
244
+	if len(errList) > 0 {
245
+		response.Fail(c, 101, errList[0])
246
+		return
247
+	}
248
+	u := f.ToOauth()
249
+	err := service.AllService.OauthService.Update(u)
250
+	if err != nil {
251
+		response.Fail(c, 101, "更新失败")
252
+		return
253
+	}
254
+	response.Success(c, nil)
255
+}
256
+
257
+// Delete 删除
258
+// @Tags Oauth
259
+// @Summary Oauth删除
260
+// @Description Oauth删除
261
+// @Accept  json
262
+// @Produce  json
263
+// @Param body body admin.OauthForm true "Oauth信息"
264
+// @Success 200 {object} response.Response
265
+// @Failure 500 {object} response.Response
266
+// @Router /admin/oauth/delete [post]
267
+// @Security token
268
+func (o *Oauth) Delete(c *gin.Context) {
269
+	f := &admin.OauthForm{}
270
+	if err := c.ShouldBindJSON(f); err != nil {
271
+		response.Fail(c, 101, "系统错误")
272
+		return
273
+	}
274
+	id := f.Id
275
+	errList := global.Validator.ValidVar(id, "required,gt=0")
276
+	if len(errList) > 0 {
277
+		response.Fail(c, 101, errList[0])
278
+		return
279
+	}
280
+	u := service.AllService.OauthService.InfoById(f.Id)
281
+	if u.Id > 0 {
282
+		err := service.AllService.OauthService.Delete(u)
283
+		if err == nil {
284
+			response.Success(c, nil)
285
+			return
286
+		}
287
+		response.Fail(c, 101, err.Error())
288
+		return
289
+	}
290
+	response.Fail(c, 101, "信息不存在")
291
+}

+ 34 - 0
http/controller/admin/user.go

@@ -259,3 +259,37 @@ func (ct *User) ChangeCurPwd(c *gin.Context) {
259 259
 	}
260 260
 	response.Success(c, nil)
261 261
 }
262
+
263
+// MyOauth
264
+// @Tags 用户
265
+// @Summary 我的授权
266
+// @Description 我的授权
267
+// @Accept  json
268
+// @Produce  json
269
+// @Success 200 {object} response.Response{data=[]adResp.UserOauthItem}
270
+// @Failure 500 {object} response.Response
271
+// @Router /admin/user/myOauth [get]
272
+// @Security token
273
+func (ct *User) MyOauth(c *gin.Context) {
274
+	u := service.AllService.UserService.CurUser(c)
275
+	oal := service.AllService.OauthService.List(1, 100, nil)
276
+	ops := make([]string, 0)
277
+	for _, oa := range oal.Oauths {
278
+		ops = append(ops, oa.Op)
279
+	}
280
+	uts := service.AllService.UserService.UserThirdsByUserId(u.Id)
281
+	var res []*adResp.UserOauthItem
282
+	for _, oa := range oal.Oauths {
283
+		item := &adResp.UserOauthItem{
284
+			ThirdType: oa.Op,
285
+		}
286
+		for _, ut := range uts {
287
+			if ut.ThirdType == oa.Op {
288
+				item.Status = 1
289
+				break
290
+			}
291
+		}
292
+		res = append(res, item)
293
+	}
294
+	response.Success(c, res)
295
+}

+ 0 - 4
http/controller/api/ab.go

@@ -7,7 +7,6 @@ import (
7 7
 	"Gwen/model"
8 8
 	"Gwen/service"
9 9
 	"encoding/json"
10
-	"fmt"
11 10
 	"github.com/gin-gonic/gin"
12 11
 	"net/http"
13 12
 )
@@ -66,7 +65,6 @@ func (a *Ab) UpAb(c *gin.Context) {
66 65
 	abf := &requstform.AddressBookForm{}
67 66
 	err := c.ShouldBindJSON(&abf)
68 67
 	if err != nil {
69
-		fmt.Println(err)
70 68
 		response.Error(c, "参数错误")
71 69
 		return
72 70
 	}
@@ -93,7 +91,6 @@ func (a *Ab) UpAb(c *gin.Context) {
93 91
 	tc := map[string]uint{}
94 92
 	err = json.Unmarshal([]byte(abd.TagColors), &tc)
95 93
 	if err != nil {
96
-		fmt.Println(err)
97 94
 		response.Error(c, "系统错误")
98 95
 		return
99 96
 	} else {
@@ -134,7 +131,6 @@ func (a *Ab) TagAdd(c *gin.Context) {
134 131
 	t := &model.Tag{}
135 132
 	err := c.ShouldBindJSON(t)
136 133
 	if err != nil {
137
-		fmt.Println(err)
138 134
 		response.Error(c, "参数错误")
139 135
 		return
140 136
 

+ 42 - 6
http/controller/api/login.go

@@ -5,7 +5,9 @@ import (
5 5
 	"Gwen/http/request/api"
6 6
 	"Gwen/http/response"
7 7
 	apiResp "Gwen/http/response/api"
8
+	"Gwen/model"
8 9
 	"Gwen/service"
10
+	"encoding/json"
9 11
 	"github.com/gin-gonic/gin"
10 12
 	"net/http"
11 13
 )
@@ -26,8 +28,9 @@ type Login struct {
26 28
 func (l *Login) Login(c *gin.Context) {
27 29
 	f := &api.LoginForm{}
28 30
 	err := c.ShouldBindJSON(f)
31
+	//fmt.Println(f)
29 32
 	if err != nil {
30
-		response.Error(c, "系统错误")
33
+		response.Error(c, "参数错误")
31 34
 		return
32 35
 	}
33 36
 
@@ -44,7 +47,20 @@ func (l *Login) Login(c *gin.Context) {
44 47
 		return
45 48
 	}
46 49
 
47
-	ut := service.AllService.UserService.Login(u)
50
+	//根据refer判断是webclient还是app
51
+	ref := c.GetHeader("referer")
52
+	if ref != "" {
53
+		f.DeviceInfo.Type = "webclient"
54
+	}
55
+
56
+	ut := service.AllService.UserService.Login(u, &model.LoginLog{
57
+		UserId:   u.Id,
58
+		Client:   f.DeviceInfo.Type,
59
+		Uuid:     f.Uuid,
60
+		Ip:       c.ClientIP(),
61
+		Type:     model.LoginLogTypeAccount,
62
+		Platform: f.DeviceInfo.Os,
63
+	})
48 64
 
49 65
 	c.JSON(http.StatusOK, apiResp.LoginRes{
50 66
 		AccessToken: ut.Token,
@@ -63,11 +79,31 @@ func (l *Login) Login(c *gin.Context) {
63 79
 // @Failure 500 {object} response.ErrorResponse
64 80
 // @Router /login-options [post]
65 81
 func (l *Login) LoginOptions(c *gin.Context) {
66
-	test := []string{
67
-		//"common-oidc/[{\"name\":\"google\"},{\"name\":\"github\"},{\"name\":\"facebook\"},{\"name\":\"网页授权登录\",\"icon\":\"\"}]",
68
-		//"oidc/myapp",
82
+	oauthOks := []string{}
83
+	err, _ := service.AllService.OauthService.GetOauthConfig(model.OauthTypeGithub)
84
+	if err == nil {
85
+		oauthOks = append(oauthOks, model.OauthTypeGithub)
86
+	}
87
+	err, _ = service.AllService.OauthService.GetOauthConfig(model.OauthTypeGoogle)
88
+	if err == nil {
89
+		oauthOks = append(oauthOks, model.OauthTypeGoogle)
90
+	}
91
+	oauthOks = append(oauthOks, model.OauthTypeWebauth)
92
+	var oidcItems []map[string]string
93
+	for _, v := range oauthOks {
94
+		oidcItems = append(oidcItems, map[string]string{"name": v})
95
+	}
96
+	common, err := json.Marshal(oidcItems)
97
+	if err != nil {
98
+		response.Error(c, "参数错误")
99
+		return
100
+	}
101
+	var res []string
102
+	res = append(res, "common-oidc/"+string(common))
103
+	for _, v := range oauthOks {
104
+		res = append(res, "oidc/"+v)
69 105
 	}
70
-	c.JSON(http.StatusOK, test)
106
+	c.JSON(http.StatusOK, res)
71 107
 }
72 108
 
73 109
 // Logout

+ 222 - 0
http/controller/api/ouath.go

@@ -0,0 +1,222 @@
1
+package api
2
+
3
+import (
4
+	"Gwen/global"
5
+	"Gwen/http/request/api"
6
+	"Gwen/http/response"
7
+	apiResp "Gwen/http/response/api"
8
+	"Gwen/model"
9
+	"Gwen/service"
10
+	"github.com/gin-gonic/gin"
11
+	"net/http"
12
+	"strconv"
13
+)
14
+
15
+type Oauth struct {
16
+}
17
+
18
+// OidcAuth
19
+// @Tags Oauth
20
+// @Summary OidcAuth
21
+// @Description OidcAuth
22
+// @Accept  json
23
+// @Produce  json
24
+// @Success 200 {object} apiResp.LoginRes
25
+// @Failure 500 {object} response.ErrorResponse
26
+// @Router /oidc/auth [post]
27
+func (o *Oauth) OidcAuth(c *gin.Context) {
28
+	f := &api.OidcAuthRequest{}
29
+	err := c.ShouldBindJSON(&f)
30
+	if err != nil {
31
+		response.Error(c, "参数错误")
32
+		return
33
+	}
34
+	if f.Op != model.OauthTypeWebauth && f.Op != model.OauthTypeGoogle && f.Op != model.OauthTypeGithub {
35
+		response.Error(c, "参数错误")
36
+		return
37
+	}
38
+
39
+	err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
40
+	if err != nil {
41
+		response.Error(c, err.Error())
42
+		return
43
+	}
44
+
45
+	service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
46
+		Action:     service.OauthActionTypeLogin,
47
+		Id:         f.Id,
48
+		Op:         f.Op,
49
+		Uuid:       f.Uuid,
50
+		DeviceName: f.DeviceInfo.Name,
51
+		DeviceOs:   f.DeviceInfo.Os,
52
+		DeviceType: f.DeviceInfo.Type,
53
+	}, 5*60)
54
+	//fmt.Println("code url", code, url)
55
+	c.JSON(http.StatusOK, gin.H{
56
+		"code": code,
57
+		"url":  url,
58
+	})
59
+}
60
+
61
+// OidcAuthQuery
62
+// @Tags Oauth
63
+// @Summary OidcAuthQuery
64
+// @Description OidcAuthQuery
65
+// @Accept  json
66
+// @Produce  json
67
+// @Success 200 {object} apiResp.LoginRes
68
+// @Failure 500 {object} response.ErrorResponse
69
+// @Router /oidc/auth-query [get]
70
+func (o *Oauth) OidcAuthQuery(c *gin.Context) {
71
+	q := &api.OidcAuthQuery{}
72
+	err := c.ShouldBindQuery(q)
73
+	if err != nil {
74
+		response.Error(c, "参数错误")
75
+		return
76
+	}
77
+	v := service.AllService.OauthService.GetOauthCache(q.Code)
78
+	if v == nil {
79
+		response.Error(c, "授权已过期,请重新授权")
80
+		return
81
+	}
82
+	if v.UserId == 0 {
83
+		//正在授权
84
+		c.JSON(http.StatusOK, gin.H{})
85
+		return
86
+	}
87
+	u := service.AllService.UserService.InfoById(v.UserId)
88
+	//fmt.Println("auth success u", u)
89
+	if u.Id > 0 {
90
+		service.AllService.OauthService.DeleteOauthCache(q.Code)
91
+		ut := service.AllService.UserService.Login(u, &model.LoginLog{
92
+			UserId:   u.Id,
93
+			Client:   v.DeviceType,
94
+			Uuid:     v.Uuid,
95
+			Ip:       c.ClientIP(),
96
+			Type:     model.LoginLogTypeOauth,
97
+			Platform: v.DeviceOs,
98
+		})
99
+		c.JSON(http.StatusOK, apiResp.LoginRes{
100
+			AccessToken: ut.Token,
101
+			Type:        "access_token",
102
+			User:        *(&apiResp.UserPayload{}).FromUser(u),
103
+		})
104
+		return
105
+	}
106
+	response.Error(c, "用户不存在")
107
+}
108
+
109
+// OauthCallback 回调
110
+// @Tags Oauth
111
+// @Summary OauthCallback
112
+// @Description OauthCallback
113
+// @Accept  json
114
+// @Produce  json
115
+// @Success 200 {object} apiResp.LoginRes
116
+// @Failure 500 {object} response.ErrorResponse
117
+// @Router /oauth/callback [get]
118
+func (o *Oauth) OauthCallback(c *gin.Context) {
119
+	state := c.Query("state")
120
+	if state == "" {
121
+		c.String(http.StatusInternalServerError, "state为空")
122
+		return
123
+	}
124
+
125
+	cacheKey := state
126
+	//从缓存中获取
127
+	v := service.AllService.OauthService.GetOauthCache(cacheKey)
128
+	if v == nil {
129
+		c.String(http.StatusInternalServerError, "授权已过期,请重新授权")
130
+		return
131
+	}
132
+
133
+	ty := v.Op
134
+	ac := v.Action
135
+	//fmt.Println("ty ac ", ty, ac)
136
+	if ty == model.OauthTypeGithub {
137
+		code := c.Query("code")
138
+		err, userData := service.AllService.OauthService.GithubCallback(code)
139
+		if err != nil {
140
+			c.String(http.StatusInternalServerError, "授权失败:"+err.Error())
141
+			return
142
+		}
143
+		if ac == service.OauthActionTypeBind {
144
+			//fmt.Println("bind", ty, userData)
145
+			utr := service.AllService.OauthService.UserThirdInfo(ty, strconv.Itoa(userData.Id))
146
+			if utr.UserId > 0 {
147
+				c.String(http.StatusInternalServerError, "已经绑定其他账号")
148
+				return
149
+			}
150
+			//绑定
151
+			u := service.AllService.UserService.InfoById(v.UserId)
152
+			if u == nil {
153
+				c.String(http.StatusInternalServerError, "用户不存在")
154
+				return
155
+			}
156
+			//绑定github
157
+			err = service.AllService.OauthService.BindGithubUser(strconv.Itoa(userData.Id), userData.Login, v.UserId)
158
+			if err != nil {
159
+				c.String(http.StatusInternalServerError, "绑定失败")
160
+				return
161
+			}
162
+			c.String(http.StatusOK, "绑定成功")
163
+			return
164
+		}
165
+		//登录
166
+		if ac == service.OauthActionTypeLogin {
167
+			if v.UserId != 0 {
168
+				c.String(http.StatusInternalServerError, "授权已经成功")
169
+				return
170
+			}
171
+			u := service.AllService.UserService.InfoByGithubId(strconv.Itoa(userData.Id))
172
+			if u == nil {
173
+				oa := service.AllService.OauthService.InfoByOp(ty)
174
+				if !*oa.AutoRegister {
175
+					//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
176
+					v.ThirdName = userData.Login
177
+					v.ThirdOpenId = strconv.Itoa(userData.Id)
178
+					url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
179
+					c.Redirect(http.StatusFound, url)
180
+					return
181
+				}
182
+
183
+				//自动注册
184
+				u = service.AllService.UserService.RegisterByGithub(userData.Login, int64(userData.Id))
185
+				if u.Id == 0 {
186
+					c.String(http.StatusInternalServerError, "注册失败")
187
+					return
188
+				}
189
+			}
190
+
191
+			v.UserId = u.Id
192
+			service.AllService.OauthService.SetOauthCache(cacheKey, v, 0)
193
+			c.String(http.StatusOK, "授权成功")
194
+			return
195
+		}
196
+
197
+		//返回js
198
+		c.Header("Content-Type", "text/html; charset=utf-8")
199
+		c.String(http.StatusOK, "授权错误")
200
+		//up := &apiResp.UserPayload{}
201
+		//c.JSON(http.StatusOK, apiResp.LoginRes{
202
+		//	AccessToken: ut.Token,
203
+		//	Type:        "access_token",
204
+		//	User:        *up.FromUser(u),
205
+		//})
206
+
207
+	}
208
+
209
+}
210
+
211
+// WebOauthLogin
212
+// @Tags Oauth
213
+// @Summary WebOauthLogin
214
+// @Description WebOauthLogin
215
+// @Accept  json
216
+// @Produce  json
217
+// @Success 200 {string} string
218
+// @Failure 500 {string} string
219
+// @Router /oauth/login [get]
220
+func (o *Oauth) WebOauthLogin(c *gin.Context) {
221
+
222
+}

+ 5 - 5
http/controller/api/user.go

@@ -21,11 +21,11 @@ type User struct {
21 21
 // @Failure 500 {object} response.Response
22 22
 // @Router /currentUser [get]
23 23
 // @Security token
24
-func (u *User) currentUser(c *gin.Context) {
25
-	user := service.AllService.UserService.CurUser(c)
26
-	up := (&apiResp.UserPayload{}).FromUser(user)
27
-	c.JSON(http.StatusOK, up)
28
-}
24
+//func (u *User) currentUser(c *gin.Context) {
25
+//	user := service.AllService.UserService.CurUser(c)
26
+//	up := (&apiResp.UserPayload{}).FromUser(user)
27
+//	c.JSON(http.StatusOK, up)
28
+//}
29 29
 
30 30
 // Info 用户信息
31 31
 // @Tags 用户

+ 1 - 1
http/controller/api/webClient.go

@@ -36,7 +36,7 @@ func (i *WebClient) ServerConfig(c *gin.Context) {
36 36
 		gin.H{
37 37
 			"id_server": global.Config.Rustdesk.IdServer,
38 38
 			"key":       global.Config.Rustdesk.Key,
39
-			//"peers":     peers,
39
+			"peers":     peers,
40 40
 		},
41 41
 	)
42 42
 }

+ 7 - 1
http/controller/web/index.go

@@ -53,7 +53,13 @@ const autoWriteServer = () => {
53 53
 							}	
54 54
                          
55 55
 						  if (res.data.peers) {
56
-							  localStorage.setItem('peers', JSON.stringify(res.data.peers))
56
+								oldPeers = JSON.parse(localStorage.getItem('peers')) || {}
57
+							  Object.keys(res.data.peers).forEach(k => {
58
+								if(!oldPeers[k]) {
59
+									oldPeers[k] = res.data.peers[k]
60
+								}
61
+                              })
62
+							  localStorage.setItem('peers', JSON.stringify(oldPeers))
57 63
 					      }
58 64
                       }
59 65
                   })

+ 11 - 0
http/http.go

@@ -7,12 +7,23 @@ import (
7 7
 	"github.com/gin-gonic/gin"
8 8
 	"github.com/sirupsen/logrus"
9 9
 	"net/http"
10
+	"strings"
10 11
 )
11 12
 
12 13
 func ApiInit() {
13 14
 	gin.SetMode(global.Config.Gin.Mode)
14 15
 	g := gin.New()
15 16
 
17
+	//[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
18
+	//Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
19
+	if global.Config.Gin.TrustProxy != "" {
20
+		pro := strings.Split(global.Config.Gin.TrustProxy, ",")
21
+		err := g.SetTrustedProxies(pro)
22
+		if err != nil {
23
+			panic(err)
24
+		}
25
+	}
26
+
16 27
 	if global.Config.Gin.Mode == gin.ReleaseMode {
17 28
 		//修改gin Recovery日志 输出为logger的输出点
18 29
 		if global.Logger != nil {

+ 7 - 0
http/middleware/rustauth.go

@@ -17,6 +17,13 @@ func RustAuth() gin.HandlerFunc {
17 17
 			c.Abort()
18 18
 			return
19 19
 		}
20
+		if len(token) <= 7 {
21
+			c.JSON(401, gin.H{
22
+				"error": "Unauthorized",
23
+			})
24
+			c.Abort()
25
+			return
26
+		}
20 27
 		//提取token,格式是Bearer {token}
21 28
 		//这里只是简单的提取
22 29
 		token = token[7:]

+ 7 - 0
http/request/admin/login.go

@@ -3,4 +3,11 @@ package admin
3 3
 type Login struct {
4 4
 	Username string `json:"username" validate:"required" label:"用户名"`
5 5
 	Password string `json:"password,omitempty" validate:"required" label:"密码"`
6
+	Platform string `json:"platform" label:"平台"`
7
+}
8
+
9
+type LoginLogQuery struct {
10
+	UserId int `form:"user_id"`
11
+	IsMy   int `form:"is_my"`
12
+	PageQuery
6 13
 }

+ 34 - 0
http/request/admin/oauth.go

@@ -0,0 +1,34 @@
1
+package admin
2
+
3
+import "Gwen/model"
4
+
5
+type BindOauthForm struct {
6
+	Op string `json:"op" binding:"required"`
7
+}
8
+
9
+type OauthConfirmForm struct {
10
+	Code string `json:"code" binding:"required"`
11
+}
12
+type UnBindOauthForm struct {
13
+	Op string `json:"op" binding:"required"`
14
+}
15
+type OauthForm struct {
16
+	Id           uint   `json:"id"`
17
+	Op           string `json:"op" validate:"required"`
18
+	ClientId     string `json:"client_id" validate:"required"`
19
+	ClientSecret string `json:"client_secret" validate:"required"`
20
+	RedirectUrl  string `json:"redirect_url" validate:"required"`
21
+	AutoRegister *bool  `json:"auto_register"`
22
+}
23
+
24
+func (of *OauthForm) ToOauth() *model.Oauth {
25
+	oa := &model.Oauth{
26
+		Op:           of.Op,
27
+		ClientId:     of.ClientId,
28
+		ClientSecret: of.ClientSecret,
29
+		RedirectUrl:  of.RedirectUrl,
30
+		AutoRegister: of.AutoRegister,
31
+	}
32
+	oa.Id = of.Id
33
+	return oa
34
+}

+ 14 - 0
http/request/api/oauth.go

@@ -0,0 +1,14 @@
1
+package api
2
+
3
+type OidcAuthRequest struct {
4
+	DeviceInfo DeviceInfoInLogin `json:"deviceInfo" label:"设备信息"`
5
+	Id         string            `json:"id"  label:"id"`
6
+	Op         string            `json:"op" label:"op"`
7
+	Uuid       string            `json:"uuid"  label:"uuid"`
8
+}
9
+
10
+type OidcAuthQuery struct {
11
+	Code string `json:"code" form:"code" label:"code"`
12
+	Id   string `json:"id" form:"id" label:"id"`
13
+	Uuid string `json:"uuid" form:"uuid" label:"uuid"`
14
+}

+ 14 - 2
http/request/api/user.go

@@ -21,9 +21,21 @@ package api
21 21
 	  bytes hwid = 14;
22 22
 	}
23 23
 */
24
+
25
+type DeviceInfoInLogin struct {
26
+	Name string `json:"name" label:"name"`
27
+	Os   string `json:"os" label:"os"`
28
+	Type string `json:"type" label:"type"`
29
+}
30
+
24 31
 type LoginForm struct {
25
-	Username string `json:"username" validate:"required,gte=4,lte=10" label:"用户名"`
26
-	Password string `json:"password,omitempty" validate:"gte=4,lte=20" label:"密码"`
32
+	AutoLogin  bool              `json:"autoLogin" label:"自动登录"`
33
+	DeviceInfo DeviceInfoInLogin `json:"deviceInfo" label:"设备信息"`
34
+	Id         string            `json:"id"  label:"id"`
35
+	Type       string            `json:"type"  label:"type"`
36
+	Uuid       string            `json:"uuid"  label:"uuid"`
37
+	Username   string            `json:"username" validate:"required,gte=4,lte=10" label:"用户名"`
38
+	Password   string            `json:"password,omitempty" validate:"gte=4,lte=20" label:"密码"`
27 39
 }
28 40
 
29 41
 type UserListQuery struct {

+ 6 - 1
http/response/admin/user.go

@@ -8,6 +8,11 @@ type LoginPayload struct {
8 8
 }
9 9
 
10 10
 var UserRouteNames = []string{
11
-	"MyTagList", "MyAddressBookList",
11
+	"MyTagList", "MyAddressBookList", "MyInfo",
12 12
 }
13 13
 var AdminRouteNames = []string{"*"}
14
+
15
+type UserOauthItem struct {
16
+	ThirdType string `json:"third_type"`
17
+	Status    int    `json:"status"`
18
+}

+ 9 - 7
http/response/api/user.go

@@ -19,17 +19,19 @@ UserStatus status;
19 19
 bool isAdmin = false;
20 20
 */
21 21
 type UserPayload struct {
22
-	Name    string `json:"name"`
23
-	Email   string `json:"email"`
24
-	Note    string `json:"note"`
25
-	IsAdmin *bool  `json:"is_admin"`
26
-	Status  int    `json:"status"`
22
+	Name    string                 `json:"name"`
23
+	Email   string                 `json:"email"`
24
+	Note    string                 `json:"note"`
25
+	IsAdmin *bool                  `json:"is_admin"`
26
+	Status  int                    `json:"status"`
27
+	Info    map[string]interface{} `json:"info"`
27 28
 }
28 29
 
29 30
 func (up *UserPayload) FromUser(user *model.User) *UserPayload {
30 31
 	up.Name = user.Username
31 32
 	up.IsAdmin = user.IsAdmin
32 33
 	up.Status = int(user.Status)
34
+	up.Info = map[string]interface{}{}
33 35
 	return up
34 36
 }
35 37
 
@@ -50,6 +52,6 @@ type LoginRes struct {
50 52
 	Type        string      `json:"type"`
51 53
 	AccessToken string      `json:"access_token"`
52 54
 	User        UserPayload `json:"user"`
53
-	Secret      string      `json:"secret"`
54
-	TfaType     string      `json:"tfa_type"`
55
+	Secret      string      `json:"secret,omitempty"`
56
+	TfaType     string      `json:"tfa_type,omitempty"`
55 57
 }

+ 2 - 28
http/response/api/webClient.go

@@ -5,33 +5,6 @@ import (
5 5
 	"time"
6 6
 )
7 7
 
8
-//	type T struct {
9
-//		Field1 struct {
10
-//			ViewStyle string `json:"view-style"`
11
-//			Tm        int64  `json:"tm"`
12
-//			Info      struct {
13
-//				Username string `json:"username"`
14
-//				Hostname string `json:"hostname"`
15
-//				Platform string `json:"platform"`
16
-//				Displays []struct {
17
-//					X      int    `json:"x"`
18
-//					Y      int    `json:"y"`
19
-//					Width  int    `json:"width"`
20
-//					Height int    `json:"height"`
21
-//					Name   string `json:"name"`
22
-//					Online bool   `json:"online"`
23
-//				} `json:"displays"`
24
-//				CurrentDisplay int    `json:"current_display"`
25
-//				SasEnabled     bool   `json:"sas_enabled"`
26
-//				Version        string `json:"version"`
27
-//				ConnId         int    `json:"conn_id"`
28
-//				Features       struct {
29
-//					PrivacyMode bool `json:"privacy_mode"`
30
-//				} `json:"features"`
31
-//			} `json:"info"`
32
-//		} `json:"1799928825"`
33
-//	}
34
-
35 8
 type WebClientPeerPayload struct {
36 9
 	ViewStyle string                   `json:"view-style"`
37 10
 	Tm        int64                    `json:"tm"`
@@ -46,7 +19,8 @@ type WebClientPeerInfoPayload struct {
46 19
 
47 20
 func (wcpp *WebClientPeerPayload) FromAddressBook(a *model.AddressBook) {
48 21
 	wcpp.ViewStyle = "shrink"
49
-	wcpp.Tm = time.Now().UnixNano()
22
+	//24小时前
23
+	wcpp.Tm = time.Now().Add(-time.Hour * 24).UnixNano()
50 24
 	wcpp.Info = WebClientPeerInfoPayload{
51 25
 		Username: a.Username,
52 26
 		Hostname: a.Hostname,

+ 32 - 0
http/router/admin.go

@@ -25,6 +25,8 @@ func Init(g *gin.Engine) {
25 25
 	TagBind(adg)
26 26
 	AddressBookBind(adg)
27 27
 	PeerBind(adg)
28
+	OauthBind(adg)
29
+	LoginLogBind(adg)
28 30
 
29 31
 	rs := &admin.Rustdesk{}
30 32
 	adg.GET("/server-config", rs.ServerConfig)
@@ -44,6 +46,7 @@ func UserBind(rg *gin.RouterGroup) {
44 46
 		cont := &admin.User{}
45 47
 		aR.GET("/current", cont.Current)
46 48
 		aR.POST("/changeCurPwd", cont.ChangeCurPwd)
49
+		aR.POST("/myOauth", cont.MyOauth)
47 50
 	}
48 51
 	aRP := rg.Group("/user").Use(middleware.AdminPrivilege())
49 52
 	{
@@ -104,6 +107,35 @@ func PeerBind(rg *gin.RouterGroup) {
104 107
 	}
105 108
 }
106 109
 
110
+func OauthBind(rg *gin.RouterGroup) {
111
+	aR := rg.Group("/oauth")
112
+	{
113
+		cont := &admin.Oauth{}
114
+		aR.POST("/confirm", cont.Confirm)
115
+		aR.POST("/bind", cont.ToBind)
116
+		aR.POST("/bindConfirm", cont.BindConfirm)
117
+		aR.POST("/unbind", cont.Unbind)
118
+		aR.GET("/info", cont.Info)
119
+	}
120
+	arp := aR.Use(middleware.AdminPrivilege())
121
+	{
122
+		cont := &admin.Oauth{}
123
+		arp.GET("/list", cont.List)
124
+		arp.GET("/detail/:id", cont.Detail)
125
+		arp.POST("/create", cont.Create)
126
+		arp.POST("/update", cont.Update)
127
+		arp.POST("/delete", cont.Delete)
128
+
129
+	}
130
+
131
+}
132
+func LoginLogBind(rg *gin.RouterGroup) {
133
+	aR := rg.Group("/login_log")
134
+	cont := &admin.LoginLog{}
135
+	aR.GET("/list", cont.List)
136
+	aR.POST("/delete", cont.Delete)
137
+}
138
+
107 139
 /*
108 140
 func FileBind(rg *gin.RouterGroup) {
109 141
 	aR := rg.Group("/file")

+ 11 - 1
http/router/api.go

@@ -19,7 +19,7 @@ func ApiInit(g *gin.Engine) {
19 19
 
20 20
 	frg := g.Group("/api")
21 21
 
22
-	frg.Use(middleware.Cors())
22
+	//frg.Use(middleware.Cors())
23 23
 	frg.OPTIONS("/*any", nil)
24 24
 
25 25
 	i := &api.Index{}
@@ -34,6 +34,16 @@ func ApiInit(g *gin.Engine) {
34 34
 		frg.POST("/login", l.Login)
35 35
 
36 36
 	}
37
+	{
38
+		o := &api.Oauth{}
39
+		// [method:POST] [uri:/api/oidc/auth]
40
+		frg.POST("/oidc/auth", o.OidcAuth)
41
+		// [method:GET] [uri:/api/oidc/auth-query?code=abc&id=xxxxx&uuid=xxxxx]
42
+		frg.GET("/oidc/auth-query", o.OidcAuthQuery)
43
+		//api/oauth/callback
44
+		frg.GET("/oauth/callback", o.OauthCallback)
45
+		frg.GET("/oauth/login", o.OauthCallback)
46
+	}
37 47
 	{
38 48
 		pe := &api.Peer{}
39 49
 		//提交系统信息

+ 23 - 0
model/loginLog.go

@@ -0,0 +1,23 @@
1
+package model
2
+
3
+type LoginLog struct {
4
+	IdModel
5
+	UserId   uint   `json:"user_id"`
6
+	Client   string `json:"client"` //webadmin,webclient,app,
7
+	Uuid     string `json:"uuid"`
8
+	Ip       string `json:"ip"`
9
+	Type     string `json:"type"`     //account,oauth
10
+	Platform string `json:"platform"` //windows,linux,mac,android,ios
11
+
12
+	TimeModel
13
+}
14
+
15
+const (
16
+	LoginLogTypeAccount = "account"
17
+	LoginLogTypeOauth   = "oauth"
18
+)
19
+
20
+type LoginLogList struct {
21
+	LoginLogs []*LoginLog `json:"list"`
22
+	Pagination
23
+}

+ 22 - 0
model/oauth.go

@@ -0,0 +1,22 @@
1
+package model
2
+
3
+type Oauth struct {
4
+	IdModel
5
+	Op           string `json:"op"`
6
+	ClientId     string `json:"client_id"`
7
+	ClientSecret string `json:"client_secret"`
8
+	RedirectUrl  string `json:"redirect_url"`
9
+	AutoRegister *bool  `json:"auto_register"`
10
+	TimeModel
11
+}
12
+
13
+const (
14
+	OauthTypeGithub  = "github"
15
+	OauthTypeGoogle  = "google"
16
+	OauthTypeWebauth = "webauth"
17
+)
18
+
19
+type OauthList struct {
20
+	Oauths []*Oauth `json:"list"`
21
+	Pagination
22
+}

+ 12 - 0
model/userThird.go

@@ -0,0 +1,12 @@
1
+package model
2
+
3
+type UserThird struct {
4
+	IdModel
5
+	UserId     uint   `json:"user_id" gorm:"not null;index"`
6
+	OpenId     string `json:"open_id" gorm:"not null;index"`
7
+	UnionId    string `json:"union_id" gorm:"not null;"`
8
+	ThirdType  string `json:"third_type" gorm:"not null;"`
9
+	ThirdEmail string `json:"third_email"`
10
+	ThirdName  string `json:"third_name"`
11
+	TimeModel
12
+}

+ 45 - 0
service/loginLog.go

@@ -0,0 +1,45 @@
1
+package service
2
+
3
+import (
4
+	"Gwen/global"
5
+	"Gwen/model"
6
+	"gorm.io/gorm"
7
+)
8
+
9
+type LoginLogService struct {
10
+}
11
+
12
+// InfoById 根据用户id取用户信息
13
+func (us *LoginLogService) InfoById(id uint) *model.LoginLog {
14
+	u := &model.LoginLog{}
15
+	global.DB.Where("id = ?", id).First(u)
16
+	return u
17
+}
18
+
19
+func (us *LoginLogService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *model.LoginLogList) {
20
+	res = &model.LoginLogList{}
21
+	res.Page = int64(page)
22
+	res.PageSize = int64(pageSize)
23
+	tx := global.DB.Model(&model.LoginLog{})
24
+	if where != nil {
25
+		where(tx)
26
+	}
27
+	tx.Count(&res.Total)
28
+	tx.Scopes(Paginate(page, pageSize))
29
+	tx.Find(&res.LoginLogs)
30
+	return
31
+}
32
+
33
+// Create 创建
34
+func (us *LoginLogService) Create(u *model.LoginLog) error {
35
+	res := global.DB.Create(u).Error
36
+	return res
37
+}
38
+func (us *LoginLogService) Delete(u *model.LoginLog) error {
39
+	return global.DB.Delete(u).Error
40
+}
41
+
42
+// Update 更新
43
+func (us *LoginLogService) Update(u *model.LoginLog) error {
44
+	return global.DB.Model(u).Updates(u).Error
45
+}

+ 256 - 0
service/oauth.go

@@ -0,0 +1,256 @@
1
+package service
2
+
3
+import (
4
+	"Gwen/global"
5
+	"Gwen/model"
6
+	"Gwen/utils"
7
+	"context"
8
+	"encoding/json"
9
+	"errors"
10
+	"fmt"
11
+	"golang.org/x/oauth2"
12
+	"golang.org/x/oauth2/github"
13
+	"golang.org/x/oauth2/google"
14
+	"gorm.io/gorm"
15
+	"io"
16
+	"strconv"
17
+	"sync"
18
+	"time"
19
+)
20
+
21
+type OauthService struct {
22
+}
23
+
24
+type GithubUserdata struct {
25
+	AvatarUrl         string      `json:"avatar_url"`
26
+	Bio               string      `json:"bio"`
27
+	Blog              string      `json:"blog"`
28
+	Collaborators     int         `json:"collaborators"`
29
+	Company           interface{} `json:"company"`
30
+	CreatedAt         time.Time   `json:"created_at"`
31
+	DiskUsage         int         `json:"disk_usage"`
32
+	Email             interface{} `json:"email"`
33
+	EventsUrl         string      `json:"events_url"`
34
+	Followers         int         `json:"followers"`
35
+	FollowersUrl      string      `json:"followers_url"`
36
+	Following         int         `json:"following"`
37
+	FollowingUrl      string      `json:"following_url"`
38
+	GistsUrl          string      `json:"gists_url"`
39
+	GravatarId        string      `json:"gravatar_id"`
40
+	Hireable          interface{} `json:"hireable"`
41
+	HtmlUrl           string      `json:"html_url"`
42
+	Id                int         `json:"id"`
43
+	Location          interface{} `json:"location"`
44
+	Login             string      `json:"login"`
45
+	Name              string      `json:"name"`
46
+	NodeId            string      `json:"node_id"`
47
+	NotificationEmail interface{} `json:"notification_email"`
48
+	OrganizationsUrl  string      `json:"organizations_url"`
49
+	OwnedPrivateRepos int         `json:"owned_private_repos"`
50
+	Plan              struct {
51
+		Collaborators int    `json:"collaborators"`
52
+		Name          string `json:"name"`
53
+		PrivateRepos  int    `json:"private_repos"`
54
+		Space         int    `json:"space"`
55
+	} `json:"plan"`
56
+	PrivateGists      int    `json:"private_gists"`
57
+	PublicGists       int    `json:"public_gists"`
58
+	PublicRepos       int    `json:"public_repos"`
59
+	ReceivedEventsUrl string `json:"received_events_url"`
60
+	ReposUrl          string `json:"repos_url"`
61
+	SiteAdmin         bool   `json:"site_admin"`
62
+	StarredUrl        string `json:"starred_url"`
63
+	SubscriptionsUrl  string `json:"subscriptions_url"`
64
+	TotalPrivateRepos int    `json:"total_private_repos"`
65
+	//TwitterUsername         interface{} `json:"twitter_username"`
66
+	TwoFactorAuthentication bool      `json:"two_factor_authentication"`
67
+	Type                    string    `json:"type"`
68
+	UpdatedAt               time.Time `json:"updated_at"`
69
+	Url                     string    `json:"url"`
70
+}
71
+
72
+type OauthCacheItem struct {
73
+	UserId      uint   `json:"user_id"`
74
+	Id          string `json:"id"` //rustdesk的设备ID
75
+	Op          string `json:"op"`
76
+	Action      string `json:"action"`
77
+	Uuid        string `json:"uuid"`
78
+	DeviceName  string `json:"device_name"`
79
+	DeviceOs    string `json:"device_os"`
80
+	DeviceType  string `json:"device_type"`
81
+	ThirdOpenId string `json:"third_open_id"`
82
+	ThirdName   string `json:"third_name"`
83
+	ThirdEmail  string `json:"third_email"`
84
+}
85
+
86
+var OauthCache = &sync.Map{}
87
+
88
+const (
89
+	OauthActionTypeLogin = "login"
90
+	OauthActionTypeBind  = "bind"
91
+)
92
+
93
+func (os *OauthService) GetOauthCache(key string) *OauthCacheItem {
94
+	v, ok := OauthCache.Load(key)
95
+	if !ok {
96
+		return nil
97
+	}
98
+	return v.(*OauthCacheItem)
99
+}
100
+
101
+func (os *OauthService) SetOauthCache(key string, item *OauthCacheItem, expire uint) {
102
+	OauthCache.Store(key, item)
103
+	if expire > 0 {
104
+		go func() {
105
+			time.Sleep(time.Duration(expire) * time.Second)
106
+			os.DeleteOauthCache(key)
107
+		}()
108
+	}
109
+}
110
+
111
+func (os *OauthService) DeleteOauthCache(key string) {
112
+	OauthCache.Delete(key)
113
+}
114
+
115
+func (os *OauthService) BeginAuth(op string) (error error, code, url string) {
116
+	code = utils.RandomString(10) + strconv.FormatInt(time.Now().Unix(), 10)
117
+
118
+	if op == model.OauthTypeWebauth {
119
+		url = global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/" + code
120
+		//url = "http://localhost:8888/_admin/#/oauth/" + code
121
+		return nil, code, url
122
+	}
123
+	err, conf := os.GetOauthConfig(op)
124
+	if err == nil {
125
+		return err, code, conf.AuthCodeURL(code)
126
+	}
127
+
128
+	return errors.New("op错误"), code, ""
129
+}
130
+
131
+// GetOauthConfig 获取配置
132
+func (os *OauthService) GetOauthConfig(op string) (error, *oauth2.Config) {
133
+	if op == model.OauthTypeGithub {
134
+		g := os.InfoByOp(model.OauthTypeGithub)
135
+		if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
136
+			return errors.New("配置不存在"), nil
137
+		}
138
+		return nil, &oauth2.Config{
139
+			ClientID:     g.ClientId,
140
+			ClientSecret: g.ClientSecret,
141
+			RedirectURL:  g.RedirectUrl,
142
+			Endpoint:     github.Endpoint,
143
+			Scopes:       []string{"read:user", "user:email"},
144
+		}
145
+	}
146
+	if op == model.OauthTypeGoogle {
147
+		g := os.InfoByOp(model.OauthTypeGoogle)
148
+		if g.Id == 0 || g.ClientId == "" || g.ClientSecret == "" || g.RedirectUrl == "" {
149
+			return errors.New("配置不存在"), nil
150
+		}
151
+		return nil, &oauth2.Config{
152
+			ClientID:     g.ClientId,
153
+			ClientSecret: g.ClientSecret,
154
+			RedirectURL:  g.RedirectUrl,
155
+			Endpoint:     google.Endpoint,
156
+			Scopes:       []string{"https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"},
157
+		}
158
+	}
159
+	return errors.New("op错误"), nil
160
+}
161
+
162
+func (os *OauthService) GithubCallback(code string) (error error, userData *GithubUserdata) {
163
+	err, oauthConfig := os.GetOauthConfig(model.OauthTypeGithub)
164
+	if err != nil {
165
+		return err, nil
166
+	}
167
+	token, err := oauthConfig.Exchange(context.Background(), code)
168
+	if err != nil {
169
+		global.Logger.Warn(fmt.Printf("oauthConfig.Exchange() failed: %s\n", err))
170
+		error = errors.New("获取token失败")
171
+		return
172
+	}
173
+
174
+	// 创建一个 HTTP 客户端,并将 access_token 添加到 Authorization 头中
175
+	client := oauthConfig.Client(context.Background(), token)
176
+	resp, err := client.Get("https://api.github.com/user")
177
+	if err != nil {
178
+		global.Logger.Warn("failed getting user info: %s\n", err)
179
+		error = errors.New("获取user info失败")
180
+		return
181
+	}
182
+	defer func(Body io.ReadCloser) {
183
+		err := Body.Close()
184
+		if err != nil {
185
+			global.Logger.Warn("failed closing response body: %s\n", err)
186
+		}
187
+	}(resp.Body)
188
+
189
+	// 在这里处理 GitHub 用户信息
190
+	if err := json.NewDecoder(resp.Body).Decode(&userData); err != nil {
191
+		global.Logger.Warn("failed decoding user info: %s\n", err)
192
+		error = errors.New("解析user info失败")
193
+		return
194
+	}
195
+	return
196
+}
197
+
198
+func (os *OauthService) UserThirdInfo(op, openid string) *model.UserThird {
199
+	ut := &model.UserThird{}
200
+	global.DB.Where("open_id = ? and third_type = ?", openid, op).First(ut)
201
+	return ut
202
+}
203
+
204
+func (os *OauthService) BindGithubUser(openid, username string, userId uint) error {
205
+	utr := &model.UserThird{
206
+		OpenId:    openid,
207
+		ThirdType: model.OauthTypeGithub,
208
+		ThirdName: username,
209
+		UserId:    userId,
210
+	}
211
+	return global.DB.Create(utr).Error
212
+}
213
+func (os *OauthService) UnBindGithubUser(userid uint) error {
214
+	return global.DB.Where("user_id = ? and third_type = ?", userid, model.OauthTypeGithub).Delete(&model.UserThird{}).Error
215
+}
216
+
217
+// InfoById 根据id取用户信息
218
+func (os *OauthService) InfoById(id uint) *model.Oauth {
219
+	u := &model.Oauth{}
220
+	global.DB.Where("id = ?", id).First(u)
221
+	return u
222
+}
223
+
224
+// InfoByOp 根据op取用户信息
225
+func (os *OauthService) InfoByOp(op string) *model.Oauth {
226
+	u := &model.Oauth{}
227
+	global.DB.Where("op = ?", op).First(u)
228
+	return u
229
+}
230
+func (os *OauthService) List(page, pageSize uint, where func(tx *gorm.DB)) (res *model.OauthList) {
231
+	res = &model.OauthList{}
232
+	res.Page = int64(page)
233
+	res.PageSize = int64(pageSize)
234
+	tx := global.DB.Model(&model.Oauth{})
235
+	if where != nil {
236
+		where(tx)
237
+	}
238
+	tx.Count(&res.Total)
239
+	tx.Scopes(Paginate(page, pageSize))
240
+	tx.Find(&res.Oauths)
241
+	return
242
+}
243
+
244
+// Create 创建
245
+func (os *OauthService) Create(u *model.Oauth) error {
246
+	res := global.DB.Create(u).Error
247
+	return res
248
+}
249
+func (os *OauthService) Delete(u *model.Oauth) error {
250
+	return global.DB.Delete(u).Error
251
+}
252
+
253
+// Update 更新
254
+func (os *OauthService) Update(u *model.Oauth) error {
255
+	return global.DB.Model(u).Updates(u).Error
256
+}

+ 2 - 0
service/service.go

@@ -13,6 +13,8 @@ type Service struct {
13 13
 	*TagService
14 14
 	*PeerService
15 15
 	*GroupService
16
+	*OauthService
17
+	*LoginLogService
16 18
 }
17 19
 
18 20
 func New() *Service {

+ 72 - 2
service/user.go

@@ -7,6 +7,8 @@ import (
7 7
 	"Gwen/utils"
8 8
 	"github.com/gin-gonic/gin"
9 9
 	"gorm.io/gorm"
10
+	"math/rand"
11
+	"strconv"
10 12
 	"time"
11 13
 )
12 14
 
@@ -51,11 +53,11 @@ func (us *UserService) InfoByAccessToken(token string) *model.User {
51 53
 
52 54
 // GenerateToken 生成token
53 55
 func (us *UserService) GenerateToken(u *model.User) string {
54
-	return utils.Md5(u.Username + u.Password + time.Now().String())
56
+	return utils.Md5(u.Username + time.Now().String())
55 57
 }
56 58
 
57 59
 // Login 登录
58
-func (us *UserService) Login(u *model.User) *model.UserToken {
60
+func (us *UserService) Login(u *model.User, llog *model.LoginLog) *model.UserToken {
59 61
 	token := us.GenerateToken(u)
60 62
 	ut := &model.UserToken{
61 63
 		UserId:    u.Id,
@@ -63,6 +65,7 @@ func (us *UserService) Login(u *model.User) *model.UserToken {
63 65
 		ExpiredAt: time.Now().Add(time.Hour * 24 * 7).Unix(),
64 66
 	}
65 67
 	global.DB.Create(ut)
68
+	global.DB.Create(llog)
66 69
 	return ut
67 70
 }
68 71
 
@@ -169,3 +172,70 @@ func (us *UserService) RouteNames(u *model.User) []string {
169 172
 	}
170 173
 	return adResp.UserRouteNames
171 174
 }
175
+
176
+// InfoByGithubId 根据githubid取用户信息
177
+func (us *UserService) InfoByGithubId(githubId string) *model.User {
178
+	ut := AllService.OauthService.UserThirdInfo(model.OauthTypeGithub, githubId)
179
+	if ut.Id == 0 {
180
+		return nil
181
+	}
182
+	u := us.InfoById(ut.UserId)
183
+	if u.Id == 0 {
184
+		return nil
185
+	}
186
+	return u
187
+}
188
+
189
+// RegisterByGithub 注册
190
+func (us *UserService) RegisterByGithub(githubName string, githubId int64) *model.User {
191
+	tx := global.DB.Begin()
192
+	ut := &model.UserThird{
193
+		OpenId:    strconv.FormatInt(githubId, 10),
194
+		ThirdName: githubName,
195
+		ThirdType: model.OauthTypeGithub,
196
+	}
197
+	//global.DB.Where("open_id = ?", githubId).First(ut)
198
+	//这种情况不应该出现,如果出现说明有bug
199
+	//if ut.Id != 0 {
200
+	//	u := &model.User{}
201
+	//	global.DB.Where("id = ?", ut.UserId).First(u)
202
+	//	tx.Commit()
203
+	//	return u
204
+	//}
205
+
206
+	username := us.GenerateUsernameByOauth(githubName)
207
+	u := &model.User{
208
+		Username: username,
209
+		GroupId:  1,
210
+	}
211
+	global.DB.Create(u)
212
+
213
+	ut.UserId = u.Id
214
+	global.DB.Create(ut)
215
+
216
+	tx.Commit()
217
+	return u
218
+}
219
+
220
+// GenerateUsernameByOauth 生成用户名
221
+func (us *UserService) GenerateUsernameByOauth(name string) string {
222
+	u := &model.User{}
223
+	global.DB.Where("username = ?", name).First(u)
224
+	if u.Id == 0 {
225
+		return name
226
+	}
227
+	name = name + strconv.FormatInt(rand.Int63n(10), 10)
228
+	return us.GenerateUsernameByOauth(name)
229
+}
230
+
231
+// UserThirdsByUserId
232
+func (us *UserService) UserThirdsByUserId(userId uint) (res []*model.UserThird) {
233
+	global.DB.Where("user_id = ?", userId).Find(&res)
234
+	return res
235
+}
236
+
237
+func (us *UserService) UserThirdInfo(userId uint, op string) *model.UserThird {
238
+	ut := &model.UserThird{}
239
+	global.DB.Where("user_id = ? and third_type = ?", userId, op).First(ut)
240
+	return ut
241
+}

+ 12 - 0
utils/tools.go

@@ -4,6 +4,7 @@ import (
4 4
 	"crypto/md5"
5 5
 	"encoding/json"
6 6
 	"fmt"
7
+	"math/rand"
7 8
 	"reflect"
8 9
 	"runtime/debug"
9 10
 )
@@ -61,3 +62,14 @@ func SafeGo(f interface{}, params ...interface{}) {
61 62
 		funcValue.Call(paramsValue)
62 63
 	}()
63 64
 }
65
+
66
+// RandomString 生成随机字符串
67
+func RandomString(n int) string {
68
+	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
69
+	length := len(letterBytes)
70
+	b := make([]byte, n)
71
+	for i := range b {
72
+		b[i] = letterBytes[rand.Intn(length)]
73
+	}
74
+	return string(b)
75
+}