Compare commits

..

313 Commits

Author SHA1 Message Date
lejianwen
dbf8b23b15 fix: Config watchConfig (#135)
---
Closes: #135
2025-02-10 10:13:34 +08:00
lejianwen
79a5dd53ae fix: User disabled can not work (#133)
---
Closes: #133
2025-02-10 10:13:15 +08:00
Tao Chen
8a5b20685c fix: When OIDC and LDAP work togethar (#132 #134)
* fix OIDC create user if LDAP enable

* `newUser.GroupId = 1` for ldap

* fix
2025-02-10 10:08:49 +08:00
lejianwen
5a9c972de0 docs: Readme 2025-02-09 21:13:01 +08:00
Tao Chen
fc0e67122d docs: add LDAP info (#130) 2025-02-09 19:36:31 +08:00
lejianwen
eb642f66ca docs: Readme 2025-02-07 18:14:12 +08:00
lejianwen
8cac15f7dd style: Log time 2025-02-07 17:57:28 +08:00
lejianwen
5011e2b7c1 feat: Web sso env (#125) 2025-02-07 17:52:40 +08:00
lejianwen
b0008143b1 docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
a3c3ab5a72 style: Up conf 2025-02-07 17:52:40 +08:00
lejianwen
3a16269215 docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
151145b0c3 feat: Random Initial Password for Admin (#117) 2025-02-07 17:52:39 +08:00
lejianwen
9c794e9d4b fix(build): Fix no admin in deb (#119 #120) 2025-02-03 13:29:35 +08:00
lejianwen
01f697d279 fix(api): Add Default Token Expire (#113) 2025-02-03 13:18:08 +08:00
lejianwen
6cdc37333b style: webclient 2025-02-03 00:01:10 +08:00
Tao Chen
ae32915565 feat(ldap): Add LDAP
* rename: Admin to AdminGroup

* update

* cleanup

* tmp save group mapping

* add enableControl(not-test)

* verify username exist before create(for LDAP)

* add getAllGroupsDn()

* rename

* adminGroup

* enable TLS Verify

* init for ldap

---------

Co-authored-by: Tao Chen <iamtaochen@outlook.com>
2025-02-02 23:59:52 +08:00
lejianwen
f49457dc5b feat(webclient): Up to 1.3.7 2025-01-21 19:12:28 +08:00
lejianwen
d9e2e247ea feat(api): Add api token expire
Resolves #109
2025-01-21 18:23:28 +08:00
lejianwen
c6f2f2f150 feat(api): Add api/version
Resolves #110
2025-01-20 20:04:22 +08:00
lejianwen
56b9c66cb8 docs: Up Swagger docs 2025-01-20 19:35:47 +08:00
lejianwen
5d8a0d0e1f style: Add Start Tips 2025-01-20 13:12:45 +08:00
lejianwen
f4cb9beda5 fix(api): Change tag to alphabetical sorting
Fixes: #108
2025-01-20 13:07:41 +08:00
lejianwen
b66fc3c06d fix(docs): Api Route doc 2025-01-19 13:10:43 +08:00
lejianwen
ab2e1a9236 feat(i18n): Add ZH_TW 2025-01-19 13:10:19 +08:00
Jia-Bin
ab77b400a1 Add Traditional Chinese 2025-01-18 23:04:39 +08:00
lejianwen
eb7ab63563 docs: Up readme 2025-01-16 22:01:23 +08:00
lejianwen
4cf7d01622 docs: Up readme 2025-01-16 21:59:46 +08:00
lejianwen
a876078a9c feat(server): Rustdesk Id Server Port & Relay Server Port #104 2025-01-16 20:57:00 +08:00
lejianwen
495f2ae3c6 refactor(config): Up Config Load 2025-01-16 20:40:42 +08:00
lejianwen
4e6d11baf0 docs: Up readme 2025-01-15 21:56:04 +08:00
lejianwen
a951b982b3 fix: Jwt 2025-01-15 20:26:26 +08:00
lejianwen
a33be66504 docs: Up readme 2025-01-15 20:09:08 +08:00
lejianwen
f41b9d5887 feat!: Add JWT
- `RUSTDESK_API_JWT_KEY`如果设置,将会启用JWT,token自动续期功能将失效
- 此功能是为了server端校验token的合法性
2025-01-15 19:25:28 +08:00
lejianwen
3c608463e6 docs: Up readme 2025-01-12 23:12:00 +08:00
lejianwen
eeffbe124a docs: Up readme 2025-01-12 21:35:34 +08:00
lejianwen
d7f2d54faa feat(server): Add Rustdesk Relay Server Commands 2025-01-04 20:49:44 +08:00
lejianwen
7db4b03634 style(server): fmt print to log 2025-01-02 21:49:37 +08:00
lejianwen
77760a681a docs: Up readme 2025-01-02 17:03:07 +08:00
lejianwen
f9c1447ceb fix: Fix Dockerfile_full_s6 2024-12-31 23:33:17 +08:00
lejianwen
fb749c1902 fix(server): Fix Rustdesk Sys Command 2024-12-31 23:29:05 +08:00
lejianwen
240c44aa07 feat(server): Add Rustdesk Command
And add build full s6 image for rustdesk command
2024-12-31 23:16:15 +08:00
lejianwen
92cd8642c8 docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:36:57 +08:00
lejianwen
89d90cf919 docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:35:06 +08:00
lejianwen
920c6b6d8b docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:33:11 +08:00
lejianwen
1dd4df3a1c chore(buildTest): add start.bat to run on windows 2024-12-27 20:13:55 +08:00
lejianwen
c7d44cc253 fix(build): add start.bat to run on windows(#89) 2024-12-27 20:09:08 +08:00
lejianwen
5082ab1893 refactor(admin): Move Admin Web Route to user model 2024-12-27 19:27:33 +08:00
lejianwen
e8b2425222 feat(admin): Add My Login log 2024-12-27 19:25:59 +08:00
lejianwen
09d12cefd8 feat(admin): Support Markdown to welcome msg 2024-12-25 19:21:50 +08:00
lejianwen
5f1166965d fix(api): Get ab list when personal is disabled (#86) 2024-12-25 19:04:42 +08:00
lejianwen
0dbab182e9 fix(webclient): share fail when expire is 0
Closes: #88
2024-12-25 15:01:03 +08:00
lejianwen
512f3f99fd fix(build): fix build_test.yml 2024-12-25 14:06:29 +08:00
lejianwen
6fb4fad705 fix(build): up build.yml to build deb 2024-12-25 13:20:12 +08:00
lejianwen
fa92529e9b chore(build): up build.yml to build deb 2024-12-25 12:57:11 +08:00
Follow the wind
d6c6051a6c feat(build): 添加构建deb包相关基础 (#87)
* 添加构建deb包相关基础

* 补齐工作流,等待验证

* 修复构建时没有创建的data目录保障deb包构建

* 修复其余架构deb包构建中的依赖错误

* 修复:由于小改工作流导致写错架构的问题

* 修复拼写错误导致的目录错误

* 添加上传工件,和发布rel工作流,完成相关事务

---------

Co-authored-by: ymwl <ymwlpoolc@qq.com>
2024-12-25 12:28:51 +08:00
lejianwen
ce063bd3ac feat(webclient): v1.3.5 -> v1.3.6 2024-12-24 10:25:47 +08:00
lejianwen
4468894dfb chore(changelog): up build.yml to generate changelog 2024-12-22 14:14:51 +08:00
lejianwen
8b00b919ad chore(changelog): up build.yml to generate changelog 2024-12-22 14:04:23 +08:00
64f4a6dfac feat(i18n): Merge pull request #85 from jimmyGALLAND/trans-fr
add locale french
2024-12-22 13:57:56 +08:00
jimmyGALLAND
6faa5153b6 add locale french 2024-12-21 22:56:48 +01:00
lejianwen
a771b1e9b0 fix(webclient): remove console.log when query online by new 2024-12-21 21:49:03 +08:00
lejianwen
7750f9c54d chore(changelog): up build.yml to add changelog 2024-12-21 21:39:11 +08:00
lejianwen
b2d24ee67b docs(webclient): up readme 2024-12-21 21:31:54 +08:00
lejianwen
589a2a5123 feat(webclient): add new query_online function
There may be a loss of performance
Therefore, it is not enabled by default
2024-12-21 21:15:06 +08:00
lejianwen
184d3d357d optimize add ab from peer #84 2024-12-20 11:11:32 +08:00
lejianwen
50b3d85270 up docs and readme 2024-12-18 14:28:06 +08:00
lejianwen
09fdd34ba3 fix captcha 2024-12-18 13:51:06 +08:00
lejianwen
bba10261c5 fix captcha 2024-12-18 13:36:01 +08:00
lejianwen
46bfe54097 add show-swagger config #83 2024-12-18 12:50:09 +08:00
lejianwen
503e7a307e up docs 2024-12-18 12:44:06 +08:00
lejianwen
821b0a6faf add captcha #82 2024-12-18 12:43:55 +08:00
lejianwen
d60fdff179 split my from admin 2024-12-17 21:41:56 +08:00
lejianwen
fdd841e82a add batch add ab from peer and up my 2024-12-13 16:27:12 +08:00
lejianwen
2d6f0a116a add share record manage 2024-12-13 12:32:36 +08:00
lejianwen
bd13fe4ef4 up web client v2 2024-12-10 18:26:08 +08:00
lejianwen
6e1b208464 up README.md 2024-12-10 15:28:05 +08:00
lejianwen
76433a409e up README.md 2024-12-09 13:43:04 +08:00
lejianwen
9b4fa679c2 add batch add ab from peer
add batch update ab tags
2024-12-06 19:45:41 +08:00
lejianwen
c2ae95c4cc up api docs 2024-12-06 10:36:40 +08:00
lejianwen
b2b7f60fd5 add batch delete user token 2024-12-06 10:36:27 +08:00
lejianwen
a465888b31 up username length to 32 #70 2024-12-06 10:22:28 +08:00
lejianwen
d368bdc84c up web client v2 2024-12-04 13:43:04 +08:00
lejianwen
cdc1150505 up readme 2024-11-28 12:42:15 +08:00
32d525c53c Create LICENSE 2024-11-26 17:29:46 +08:00
lejianwen
a89b40c607 add es lang 2024-11-26 10:43:01 +08:00
lejianwen
b6bd9150d9 up web client v2 from rustdesk 2024-11-22 19:54:19 +08:00
lejianwen
96e3e3bc86 up docs 2024-11-22 19:53:49 +08:00
lejianwen
41377f41bb Split the language 2024-11-22 19:39:28 +08:00
lejianwen
fb744f81e2 up img 2024-11-20 19:33:27 +08:00
lejianwen
750c3bcbcd fix #62 2024-11-20 19:32:44 +08:00
lejianwen
d4015d7284 fix 2024-11-20 09:20:29 +08:00
lejianwen
a9bf3fda73 fix https://github.com/lejianwen/rustdesk-api/discussions/59#discussioncomment-11306760 2024-11-20 09:17:29 +08:00
lejianwen
7f467a4814 up web client v2 2024-11-18 21:39:18 +08:00
lejianwen
9f10b5e983 up readme 2024-11-17 18:27:15 +08:00
lejianwen
5291270e6a up ws connect in https #12 2024-11-17 17:34:51 +08:00
lejianwen
56bba381d8 fix 2024-11-16 22:08:06 +08:00
lejianwen
2ff276b162 up package lock 2024-11-16 20:56:26 +08:00
lejianwen
d77191ce0f up ws js 2024-11-16 20:03:30 +08:00
lejianwen
f99803aef4 up 2024-11-16 19:42:13 +08:00
lejianwen
e09fa17013 add webclientv2 2024-11-16 18:33:02 +08:00
lejianwen
c7e69aa8a5 fix gitignore 2024-11-15 21:43:23 +08:00
ljw
9548068283 up readme 2024-11-12 11:54:21 +08:00
ljw
09958c78f3 up readme 2024-11-12 11:53:57 +08:00
ljw
aced098982 add batch delete log #57 2024-11-12 09:08:10 +08:00
ljw
7862a34760 up admin conf 2024-11-12 08:43:49 +08:00
ljw
1384d28cac add admin conf 2024-11-11 22:26:15 +08:00
ljw
24b7338153 up readme 2024-11-09 20:38:11 +08:00
ljw
30d254eaef fix #55 2024-11-09 20:14:14 +08:00
ljw
bb8a936ade add build_test.yml 2024-11-08 16:09:50 +08:00
ljw
61044fd30b add build_test.yml 2024-11-08 16:05:43 +08:00
ljw
22a4546d0f add cmd 2024-11-08 15:54:49 +08:00
ljw
07450416ed fix #52 & add auto refresh token #53 2024-11-07 10:46:00 +08:00
0d6db0d2a1 Merge pull request #52 from IamTaoChen/fix/bug
fix: cannot delete user
2024-11-07 10:21:50 +08:00
Tao Chen
ab30b3407b add error information 2024-11-06 15:06:15 +08:00
Tao Chen
7a4c735803 fix: cannot delete user 2024-11-06 14:36:12 +08:00
ljw
654c764019 fix migrate 2024-11-05 21:07:39 +08:00
ljw
7101139250 fix migrate 2024-11-05 21:07:31 +08:00
ljw
793614841a fix migrate 2024-11-05 21:03:32 +08:00
ljw
94f2ac5b2a fix username length #48 2024-11-05 11:57:16 +08:00
ljw
d4d39eecaa up oauth re 2024-11-05 11:48:35 +08:00
ljw
8af01c859c Merge branch 'oauth_re' of https://github.com/IamTaoChen/rustdesk-api into IamTaoChen-oauth_re 2024-11-05 08:24:48 +08:00
Tao Chen
3af92d03e8 modify google ro re-use oidc 2024-11-04 21:30:58 +08:00
Tao Chen
f17d891302 fix: delete check 2024-11-03 22:23:24 +08:00
Tao Chen
51623436b0 fix: call us.IsAdmin(u) to check admin 2024-11-03 21:59:17 +08:00
Tao Chen
1b7c7eef8f fix google 2024-11-03 18:04:28 +08:00
Tao Chen
15e4c7e522 re-use responseLoginSuccess 2024-11-03 17:25:27 +08:00
Tao Chen
120ab132a9 fix: last admin shouldn't be deleted, disabled or demoted 2024-11-03 17:19:05 +08:00
Tao Chen
0b52e8faa8 fix: Github AvatarUrl to OauthUser 2024-11-03 16:49:28 +08:00
Tao Chen
60eaaf390a add err for RegisterByOauth 2024-11-03 16:49:03 +08:00
Tao Chen
9101badb1c fronted for docker-dev 2024-11-03 16:34:50 +08:00
Tao Chen
7082111b34 optimize /admin/login-options 2024-11-03 05:37:34 +08:00
Tao Chen
a156f2681e chroe 2024-11-03 05:34:19 +08:00
Tao Chen
7cb823c957 add Avatar to OauthUser 2024-11-03 05:33:59 +08:00
Tao Chen
817f612224 low case email 2024-11-03 05:25:10 +08:00
Tao Chen
1cbaf9d6bc const var for op name 2024-11-03 05:13:22 +08:00
Tao Chen
d353f9837f fix: email from github 2024-11-03 05:11:31 +08:00
Tao Chen
14beef72fc fix: Email of Register 2024-11-03 05:07:17 +08:00
Tao Chen
91a33fe7f6 fix: RegisterByOauth without Email 2024-11-03 04:35:39 +08:00
ljw
7ad7a0ff41 fix #45 2024-11-02 18:49:16 +08:00
Tao Chen
d646469f71 set user_id=0 at peers, when the user is deleted 2024-11-02 08:24:07 +08:00
Tao Chen
4f616ffff1 When login, peer doesn't exist, it should create 2024-11-02 08:19:44 +08:00
Tao Chen
64400fba61 delete the token when delete a peer 2024-11-02 08:02:03 +08:00
Tao Chen
fc15e8c63d add MyPeers for user 2024-11-02 07:35:26 +08:00
Tao Chen
cbba1e5317 add email for register 2024-11-02 05:43:55 +08:00
Tao Chen
75b997dcc4 add DeviceId to userToken 2024-11-02 05:42:47 +08:00
Tao Chen
853063c59b logout should unbind uuid and uid of peer 2024-11-02 05:07:41 +08:00
Tao Chen
5c65edb8fa fix bug ValidateOauthProvider location 2024-11-02 04:20:00 +08:00
Tao Chen
7707cc116f re-construct oauth 2024-11-02 04:01:28 +08:00
ljw
737fe749de load key from file 2024-11-01 14:56:11 +08:00
b9c6f17e3f Merge pull request #42 from IamTaoChen/docker
Docker Optimize
2024-11-01 13:10:59 +08:00
Tao Chen
af6a340003 add more 2024-11-01 01:33:08 +08:00
Tao Chen
8ba3bee944 optimize scripts 2024-11-01 01:33:05 +08:00
Tao Chen
153c3566b6 optimize build speed, like cache and mirror 2024-11-01 01:32:45 +08:00
Tao Chen
97a4753f4f add ARG CONTRY=CN to improve the alpinelinux install speed 2024-11-01 00:29:33 +08:00
Tao Chen
74c3899b55 add bash to run dev-docker 2024-11-01 00:28:43 +08:00
ljw
273ac6d1a8 add remove user token #34 2024-10-31 22:29:12 +08:00
5edbb39a63 Merge pull request #40 from IamTaoChen/resetEmptyPassWD
Reset empty password
2024-10-31 18:46:34 +08:00
Tao Chen
0c974c4113 Merge branch 'master' into resetEmptyPassWD 2024-10-31 16:35:32 +08:00
Tao Chen
46657a525d ommit check old passwd if password is empty 2024-10-31 16:23:06 +08:00
Tao Chen
b36aa6f917 add IsPasswordEmpty... 2024-10-31 16:22:42 +08:00
ljw
cddb0ebef9 up readme #28 2024-10-31 15:48:36 +08:00
ljw
788c4e3531 up readme #28 2024-10-31 15:47:39 +08:00
ljw
47f9ad8274 add register 2024-10-31 15:14:30 +08:00
ljw
855beb7fa9 up oauth 2024-10-31 14:03:48 +08:00
f57816b1b0 Merge pull request #36 from IamTaoChen/oidc-for-web
OIDC for web
2024-10-31 11:10:46 +08:00
Tao Chen
ff08fefc30 rename build stage 2024-10-31 09:21:43 +08:00
Tao Chen
f792ab9055 add some /admin/ to surport web OIDC 2024-10-31 09:21:30 +08:00
ljw
63af103a4e fix buidconfirm 2024-10-30 20:59:51 +08:00
ljw
0a36d44cec up del user 2024-10-30 19:34:56 +08:00
a1f4e1de84 Merge pull request #32 from IamTaoChen/bug/odic-user
delete user from user_thirds and update README
2024-10-30 19:08:50 +08:00
Tao Chen
05b20d47db modify delete user 2024-10-30 16:33:01 +08:00
Tao Chen
6b746f13d1 update README 2024-10-30 16:31:47 +08:00
Tao Chen
e838d5bcd2 update README for OIDC 2024-10-30 16:29:49 +08:00
Tao Chen
0dcc21260e delete user from user_thirds, too 2024-10-30 15:59:33 +08:00
ljw
3c30ad145c up v 2024-10-30 15:46:12 +08:00
ljw
06b0a8e873 add docker-compose-dev.yaml 2024-10-30 15:34:45 +08:00
b7de2ccadd Merge pull request #30 from IamTaoChen/oidc
Add General OIDC Login
2024-10-30 14:40:10 +08:00
Tao Chen
b52c5cfca1 bind oidc ThirdEmail 2024-10-29 23:09:54 +08:00
Tao Chen
fe910c37cf fix: spelling 2024-10-29 23:00:17 +08:00
Tao Chen
337ef330eb fix bug 2024-10-29 18:48:37 +08:00
Tao Chen
ffa47177aa fix bug - oidc scopes 2024-10-29 18:46:45 +08:00
ljw
46a76853c3 fix oauth register #26 #23 2024-10-29 15:31:27 +08:00
Tao Chen
2cd7dfb2b3 fix bug 2024-10-29 14:27:15 +08:00
Tao Chen
fee2808bca try add oidc 2024-10-29 11:51:01 +08:00
Tao Chen
49e5eb186a optimize docker 2024-10-29 11:50:55 +08:00
Tao Chen
dee2865466 optimize build.sh 2024-10-29 10:58:17 +08:00
ljw
eb340b2615 add last online ip #24 2024-10-28 20:24:34 +08:00
ljw
e714549a95 up address book add version #20 2024-10-28 19:48:47 +08:00
ljw
a1367bcd3d up peer update 2024-10-28 19:15:13 +08:00
ljw
642351dafd fix bug #27 2024-10-28 16:08:33 +08:00
ljw
90b9b5adba up 2024-10-28 14:54:00 +08:00
ljw
5bf4bbe45f add address book name &
add share address book
2024-10-28 14:51:07 +08:00
ljw
036f928fa3 up readme 2024-10-23 11:09:28 +08:00
ljw
94e7b31fb6 fix group 2024-10-23 11:09:13 +08:00
ljw
be4742382d up build.yml 2024-10-23 09:19:02 +08:00
ljw
70d2f1a055 add armv7l build #21 2024-10-23 09:03:01 +08:00
ljw
877fe50049 add ru lang 2024-10-22 19:46:38 +08:00
ljw
7d505705ee add ko lang,but validator dont have translations ko 2024-10-22 12:21:32 +08:00
ljw
38f81a03b5 Merge branch 'master' of https://github.com/lejianwen/rustdesk-api 2024-10-22 11:31:01 +08:00
d549d23819 Merge pull request #19 from jkh0kr/master
Add Korean language file
2024-10-22 11:30:49 +08:00
ljw
934675e0f0 up 2024-10-22 11:28:27 +08:00
진기환
2be397aa38 Add Korean language file 2024-10-22 10:35:58 +09:00
ljw
a0a422ed45 up readme 2024-10-21 21:35:27 +08:00
ljw
fcce10c695 add file conn log 2024-10-21 21:08:25 +08:00
ljw
30eb14702f up build.yml 2024-10-20 20:26:08 +08:00
ljw
3679fcc874 fix group create type 2024-10-20 20:15:12 +08:00
ljw
d085b4e3c2 up readme 2024-10-20 19:40:49 +08:00
dc8fcdf214 Merge pull request #17 from Ogannesson/master
Add proxy option for Google Oauthon
2024-10-20 19:05:01 +08:00
Oganneson
8bab23b65b Add oauth callback via proxy
Improved support for environment variables and configuration files, and standardized default behaviors
2024-10-20 17:56:11 +08:00
ljw
f64022e411 add conn log 2024-10-18 15:05:58 +08:00
ljw
2d37302cf9 fix write when heartbeat #14 2024-10-17 10:24:02 +08:00
ljw
1a1856257d fix write when heartbeat #14 2024-10-16 21:32:55 +08:00
ljw
24f570b64f fix pc add #13 2024-10-16 10:10:20 +08:00
ljw
6322177b71 fix pc add #13 2024-10-16 09:28:39 +08:00
ljw
d2390d1cb3 Revert "add webclient v2 preview"
This reverts commit 399c32da7d.
2024-10-15 16:35:34 +08:00
ljw
6fe6f6b708 Revert "up readme"
This reverts commit a656f4fec3.
2024-10-15 16:35:34 +08:00
ljw
a656f4fec3 up readme 2024-10-15 16:13:42 +08:00
ljw
399c32da7d add webclient v2 preview 2024-10-15 16:11:40 +08:00
ljw
62167836dc fix docs 2024-10-15 14:51:19 +08:00
ljw
caef3897a0 add login fail warn &
add web client on/off &
up admin peer filter &
upgrade web client
2024-10-14 10:43:29 +08:00
ljw
5ef6810e3f up readme 2024-10-12 10:02:04 +08:00
ljw
ae2079f583 Revert "Revert "up readme""
This reverts commit aa65382a0f.
2024-10-11 22:45:35 +08:00
ljw
aa65382a0f Revert "up readme"
This reverts commit 18c61a2bfc.
2024-10-11 22:06:20 +08:00
ljw
18c61a2bfc up readme 2024-10-11 21:39:58 +08:00
ljw
7cc1a8a58a up readme 2024-10-11 10:11:42 +08:00
ljw
cf9feac702 up readme 2024-10-10 13:00:29 +08:00
ljw
a963cd0209 fix readme 2024-10-10 12:40:54 +08:00
ljw
6a5408f9b8 up gorm logger & add share to guest by web client 2024-10-09 15:53:08 +08:00
ljw
9aad62d1e4 build tag 2024-09-29 12:47:04 +08:00
ljw
867eab40f8 build default push to docker 2024-09-29 12:39:33 +08:00
ljw
eb5c7efc4c fix build 2024-09-29 12:23:34 +08:00
857abc16e7 Merge pull request #5 from gigaion/new-build-1
build.yml - Add GHCR & Dynamic Inputs
2024-09-29 12:12:17 +08:00
ljw
28b9866c42 upgrade: init by i18n
add: batch delete peer
add: batch peer to addressbook
2024-09-29 11:53:58 +08:00
Gigaion
a27deb0a41 build.yml - Add GHCR & Dynamic Inputs
build.yml - Add GHCR & Dynamic Inputs
2024-09-28 13:30:44 -07:00
ljw
8e026de20b test 2024-09-28 11:01:23 +08:00
ljw
718ecc2372 test 2024-09-28 10:12:56 +08:00
ljw
56d46722f4 test 2024-09-28 10:05:04 +08:00
ljw
b6463cd715 test 2024-09-28 09:57:32 +08:00
ljw
bd3ae0cbfe fix build docker image 2024-09-28 09:39:26 +08:00
ljw
83c3aa894f fix build docker image 2024-09-28 09:33:59 +08:00
ljw
1b88d26fea up build 2024-09-27 22:08:36 +08:00
ljw
588287fdb4 up build 2024-09-27 22:06:04 +08:00
ljw
688e544b07 up build 2024-09-27 22:03:06 +08:00
ljw
3e3f812e83 up build 2024-09-27 21:48:48 +08:00
ljw
b551c7abe4 up build 2024-09-27 21:37:12 +08:00
ljw
6d1e7a4c05 up build 2024-09-27 21:29:19 +08:00
ljw
3341a4bc8e up build docker echo manifest 2024-09-27 21:20:54 +08:00
ljw
1c84980d36 up build docker 2024-09-27 20:10:17 +08:00
ljw
833b25881d up build docker 2024-09-27 20:06:49 +08:00
ljw
9dbf58903c up build docker 2024-09-27 19:40:48 +08:00
ljw
ad007f0d91 up build docker 2024-09-27 18:49:22 +08:00
ljw
4b06973a52 up build docker 2024-09-27 18:48:09 +08:00
ljw
159a67f15d up build docker 2024-09-27 17:49:17 +08:00
ljw
7c03b9953b up build docker 2024-09-27 17:32:25 +08:00
ljw
f90987de8d up build docker 2024-09-27 17:26:40 +08:00
ljw
70e4ff7820 up build docker 2024-09-27 17:25:30 +08:00
ljw
a99356f54b up build docker 2024-09-27 17:24:25 +08:00
ljw
c5bc9534cc up build docker 2024-09-27 17:15:06 +08:00
ljw
a40733424f up build docker 2024-09-27 16:52:02 +08:00
ljw
a937efc60e up build docker 2024-09-27 16:07:06 +08:00
ljw
6adb0e8415 up build docker 2024-09-27 15:59:12 +08:00
ljw
ff9ffb2f12 up build docker 2024-09-27 15:31:43 +08:00
ljw
9be4f472ae up build docker 2024-09-27 15:23:19 +08:00
ljw
8581d74b08 up build docker 2024-09-27 15:18:45 +08:00
ljw
dafe9bd6b6 up build docker 2024-09-27 14:58:17 +08:00
ljw
3ae5772360 up build docker 2024-09-27 14:54:11 +08:00
ljw
4628dbccfb up build docker 2024-09-27 14:46:39 +08:00
ljw
572b1d4c14 up build docker 2024-09-27 14:35:43 +08:00
ljw
bdb70e9859 up build docker 2024-09-27 14:29:55 +08:00
ljw
38bda17271 up build docker 2024-09-27 14:24:17 +08:00
ljw
455e1d2e5b up build docker 2024-09-27 14:19:56 +08:00
ljw
89cd724bab up build docker 2024-09-27 14:18:36 +08:00
ljw
b9109b4d0e up build docker 2024-09-27 14:16:59 +08:00
ljw
945958f552 up build docker 2024-09-27 14:10:50 +08:00
ljw
78eb0d5c06 up release_arm64.yml 2024-09-27 10:43:48 +08:00
ljw
bc6eae711e up release_arm64.yml 2024-09-27 10:38:14 +08:00
ljw
f0a4bf6164 up docker_arm64.yml 2024-09-26 14:36:15 +08:00
ljw
fc3b5e3ac3 up docker_arm64.yml 2024-09-26 14:33:01 +08:00
ljw
f7235ac847 up docker_arm64.yml 2024-09-26 14:26:15 +08:00
ljw
231f4ddb7f up docker_arm64.yml 2024-09-26 14:24:14 +08:00
ljw
3cad3994cb add docker_arm64.yml 2024-09-26 14:18:57 +08:00
ljw
8c97cc8686 up README 2024-09-26 13:38:43 +08:00
ljw
7ae976ee5d up README_EN.md 2024-09-26 13:21:17 +08:00
ljw
e91b53eb32 test release_arm64.yml 2024-09-26 11:49:11 +08:00
ljw
90311536a7 test release_arm64.yml 2024-09-26 11:46:17 +08:00
ljw
e951b7f2f9 fix Dockerfile 2024-09-26 09:29:02 +08:00
ljw
e0f94b62cf add i18n 2024-09-25 22:42:36 +08:00
ljw
8cb701ec85 add i18n 2024-09-25 22:41:57 +08:00
ljw
9afd11e3b8 up readme 2024-09-24 21:16:03 +08:00
ljw
a1d495f2db fix group peers 2024-09-24 19:35:20 +08:00
ljw
652fa93910 up readme 2024-09-24 15:22:21 +08:00
ljw
ef414855f0 up docs 2024-09-24 15:16:07 +08:00
ljw
76cf35cdf0 up readme 2024-09-24 15:15:26 +08:00
ljw
9d7aa05032 add personal apis 2024-09-24 14:43:27 +08:00
ljw
62a22c697d up web client can get pwd if exist 2024-09-24 10:15:04 +08:00
ljw
be497a5aa7 add docker.yml 2024-09-23 14:43:43 +08:00
ljw
fc3e16bc63 up README.md 2024-09-23 10:21:43 +08:00
ljw
df35912461 up release.yml 2024-09-23 10:13:44 +08:00
ljw
0ddc66a854 up release.yml 2024-09-23 10:10:29 +08:00
ljw
716d557d66 up test.yml 2024-09-23 10:07:10 +08:00
ljw
f9edcb9d47 up test.yml 2024-09-22 21:40:17 +08:00
ljw
d4623c5bc9 up test.yml 2024-09-22 21:28:43 +08:00
ljw
02ba4a3330 up test.yml 2024-09-22 20:30:12 +08:00
ljw
a72d4eaf78 up test.yml 2024-09-22 19:46:37 +08:00
ljw
4d3abb2dc0 up test.yml 2024-09-22 17:58:42 +08:00
ad6a8d1f7a Update test.yml 2024-09-21 22:49:06 +08:00
a5c8c2ac97 Update test.yml 2024-09-21 21:55:36 +08:00
1237004cb1 Update test.yml 2024-09-21 21:44:43 +08:00
c2bcd17df7 Update go.yml 2024-09-21 21:43:31 +08:00
bb49cbdd50 Update test.yml 2024-09-21 21:40:36 +08:00
6ad770a824 Create test.yml 2024-09-21 21:35:55 +08:00
593eeb3ac3 Update go.yml 2024-09-21 21:17:03 +08:00
6a7be3ef84 Create go.yml 2024-09-21 21:13:54 +08:00
218 changed files with 216212 additions and 4201 deletions

View File

@@ -66,7 +66,7 @@ jobs:
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.23' # 选择 Go 版本
go-version: '1.22' # 选择 Go 版本
- name: Set up npm
uses: actions/setup-node@v2
@@ -115,12 +115,12 @@ jobs:
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
else
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
wget https://musl.ljw.red/aarch64-linux-musl-cross.tgz
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xf aarch64-linux-musl-cross.tgz
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
elif [ "${{ matrix.job.platform }}" = "armv7l" ]; then
wget https://musl.ljw.red/armv7l-linux-musleabihf-cross.tgz
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
tar -xf armv7l-linux-musleabihf-cross.tgz
export PATH=$PATH:$PWD/armv7l-linux-musleabihf-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=arm GOARM=7 CC=armv7l-linux-musleabihf-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
@@ -147,7 +147,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate Changelog
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
@@ -381,7 +380,7 @@ jobs:
- name: Create and push manifest Docker Hub (:version)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
@@ -391,7 +390,7 @@ jobs:
- name: Create and push manifest GHCR (:version)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
@@ -402,7 +401,7 @@ jobs:
- name: Create and push manifest Docker Hub (:latest)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
@@ -412,7 +411,7 @@ jobs:
- name: Create and push manifest GHCR (:latest)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:latest
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-amd64,
@@ -423,7 +422,7 @@ jobs:
- name: Create and push Full S6 manifest Docker Hub (:version)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
@@ -434,7 +433,7 @@ jobs:
- name: Create and push Full S6 manifest GHCR (:latest)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,

View File

@@ -61,7 +61,7 @@ jobs:
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.23' # 选择 Go 版本
go-version: '1.22' # 选择 Go 版本
- name: Set up npm
uses: actions/setup-node@v2
@@ -101,12 +101,12 @@ jobs:
zip -r ${{ matrix.job.goos}}-${{ matrix.job.platform }}.${{matrix.job.file_ext}} ./release
else
if [ "${{ matrix.job.platform }}" = "arm64" ]; then
wget https://musl.ljw.red/aarch64-linux-musl-cross.tgz
wget https://musl.cc/aarch64-linux-musl-cross.tgz
tar -xf aarch64-linux-musl-cross.tgz
export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=${{ matrix.job.platform }} CC=aarch64-linux-musl-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
elif [ "${{ matrix.job.platform }}" = "armv7l" ]; then
wget https://musl.ljw.red/armv7l-linux-musleabihf-cross.tgz
wget https://musl.cc/armv7l-linux-musleabihf-cross.tgz
tar -xf armv7l-linux-musleabihf-cross.tgz
export PATH=$PATH:$PWD/armv7l-linux-musleabihf-cross/bin
GOOS=${{ matrix.job.goos }} GOARCH=arm GOARM=7 CC=armv7l-linux-musleabihf-gcc CGO_LDFLAGS="-static" CGO_ENABLED=1 go build -ldflags "-s -w" -o ./release/apimain ./cmd/apimain.go
@@ -317,7 +317,7 @@ jobs:
- name: Create and push manifest Docker Hub (:version)
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,
@@ -327,7 +327,7 @@ jobs:
- name: Create and push manifest GHCR (:version)
if: ${{ env.SKIP_GHCR == 'false' }}
uses: Noelware/docker-manifest-action@v0.2.3
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-amd64,

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@ runtime/*
go.sum
resources/admin
release
data/rustdeskapi.db
data

View File

@@ -42,11 +42,11 @@ RUN if [ "$COUNTRY" = "CN" ] ; then \
fi && \
apk update && apk add --no-cache git
ARG FRONTEND_GIT_REPO=https://github.com/lejianwen/rustdesk-api-web.git
ARG FREONTEND_GIT_REPO=https://github.com/lejianwen/rustdesk-api-web.git
ARG FRONTEND_GIT_BRANCH=master
# Clone the frontend repository
RUN git clone -b $FRONTEND_GIT_BRANCH $FRONTEND_GIT_REPO .
RUN git clone -b $FRONTEND_GIT_BRANCH $FREONTEND_GIT_REPO .
# Install required tools without caching index to minimize image size
RUN if [ "$COUNTRY" = "CN" ] ; then \
@@ -91,4 +91,4 @@ VOLUME /app/data
EXPOSE 21114
# Define the command to run the application
CMD ["./apimain"]
CMD ["./apimain"]

104
README.md
View File

@@ -2,26 +2,17 @@
[English Doc](README_EN.md)
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web Admin 和 Web 客户端。
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
<div align=center>
<div align=center>
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
<img src="https://img.shields.io/badge/i18n-7-green"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
</div>
## 搭配[lejianwen/rustdesk-server]使用更佳。
> [lejianwen/rustdesk-server]fork自RustDesk Server官方仓库
> 1. 解决了使用API链接超时问题
> 2. 可以强制登录后才能发起链接
> 3. 支持客户端websocket
# 特性
- PC端API
@@ -54,6 +45,7 @@
- 自动获取ID服务器和KEY
- 自动获取地址簿
- 游客通过临时分享链接直接远程到设备
- v2 Preview
- CLI
- 重置管理员密码
@@ -102,8 +94,8 @@
- 对于`OIDC`, `Issuer`是必须的。`Scopes`是可选的,默认为 `openid,profile,email`. 确保可以获取 `sub`,`email``preferred_username`
- `github oauth app``Settings`->`Developer settings`->`OAuth Apps`->`New OAuth App`
中创建,地址 [https://github.com/settings/developers](https://github.com/settings/developers)
- `Authorization callback URL`填写`http://<your server[:port]>/api/oidc/callback`
,比如`http://127.0.0.1:21114/api/oidc/callback`
- `Authorization callback URL`填写`http://<your server[:port]>/api/oauth/callback`
,比如`http://127.0.0.1:21114/api/oauth/callback`
7. 登录日志
8. 链接日志
9. 文件传输日志
@@ -116,7 +108,8 @@
* 可以官方指令
* 可以添加自定义指令
* 可以执行自定义指令
![rustdesk_command_advance](./docs/rustdesk_command_advance.png)
11. **LDAP 支持**, 当在API Server上设置了LDAP(已测试AD和LDAP),可以通过LDAP中的用户信息进行登录 https://github.com/lejianwen/rustdesk-api/issues/114 ,如果LDAP验证失败返回本地用户
@@ -126,6 +119,9 @@
2. 如果没登录后台点击右上角登录即可api server已经自动配置好了
3. 登录后会自动同步ID服务器和KEY
4. 登录后会将地址簿自动保存到web client中方便使用
5. 现已支持`v2 Preview`,访问路径是`/webclient2`
![webclientv2](./docs/webclientv2.png)
6. `v2 preview` 部署,参考[WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
### 自动化文档: 使用 Swag 生成 API 文档,方便开发者理解和使用 API。
@@ -150,11 +146,69 @@
### 相关配置
* [配置文件](./conf/config.yaml)
* 参考`conf/config.yaml`配置文件,修改相关配置。
* 如果`gorm.type``sqlite`则不需要配置mysql相关配置。
* 语言如果不设置默认为`zh-CN`
```yaml
lang: "en"
app:
web-client: 1 # 1:启用 0:禁用
register: false #是否开启注册
show-swagger: 0 #是否显示swagger文档
gin:
api-addr: "0.0.0.0:21114"
mode: "release"
resources-path: 'resources'
trust-proxy: ""
gorm:
type: "sqlite"
max-idle-conns: 10
max-open-conns: 100
mysql:
username: "root"
password: "111111"
addr: "192.168.1.66:3308"
dbname: "rustdesk"
rustdesk:
id-server: "192.168.1.66:21116"
relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114"
key: "123456789"
personal: 1
logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false
host: ""
jwt:
key: ""
expire-duration: 360000
ldap:
enable: false
url: "ldap://ldap.example.com:389"
tls: false
tls-verify: false
base-dn: "dc=example,dc=com"
bind-dn: "cn=admin,dc=example,dc=com"
bind-password: "password"
user:
base-dn: "ou=users,dc=example,dc=com"
enable-attr: "" #The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
enable-attr-value: "" # The value of the enable attribute when the user is enabled. If you are using AD, just set random value, it will be ignored.
filter: "(cn=*)"
username: "uid" # The attribute name of the user for usernamem if you are using AD, it should be "sAMAccountName"
email: "mail"
first-name: "givenName"
last-name: "sn"
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
```
### 环境变量
环境变量和配置文件`conf/config.yaml`中的配置一一对应,变量名前缀是`RUSTDESK_API`
下面表格并未全部列出,可以参考`conf/config.yaml`中的配置。
@@ -166,11 +220,7 @@
| RUSTDESK_API_APP_WEB_CLIENT | 是否启用web-client; 1:启用,0:不启用; 默认启用 | 1 |
| RUSTDESK_API_APP_REGISTER | 是否开启注册; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | 是否可见swagger文档;`1`显示,`0`不显示,默认`0`不显示 | `1` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长 | `168h` |
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | 是否禁用密码登录; `true`, `false` 默认`false` | `false` |
| RUSTDESK_API_APP_REGISTER_STATUS | 注册用户默认状态; 1 启用2 禁用, 默认 1 | `1` |
| RUSTDESK_API_APP_CAPTCHA_THRESHOLD | 验证码触发次数; -1 不启用, 0 一直启用, >0 登录错误次数后启用 ;默认 `3` | `3` |
| RUSTDESK_API_APP_BAN_THRESHOLD | 封禁IP触发次数; 0 不启用, >0 登录错误次数后封禁IP; 默认 `0` | `0` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token有效时长(秒) | `3600` |
| -----ADMIN配置----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
@@ -187,7 +237,6 @@
| RUSTDESK_API_MYSQL_PASSWORD | mysql密码 | 111111 |
| RUSTDESK_API_MYSQL_ADDR | mysql地址 | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | mysql数据库名 | rustdesk |
| RUSTDESK_API_MYSQL_TLS | 是否启用TLS, 可选值: `true`, `false`, `skip-verify`, `custom` | `false` |
| -----RUSTDESK配置----- | ---------- | ---------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk的id服务器地址 | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk的relay服务器地址 | 192.168.1.66:21117 |
@@ -195,13 +244,12 @@
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk的key | 123456789 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk存放key的文件 | `./conf/data/id_ed25519.pub` |
| RUSTDESK_API_RUSTDESK_WEBCLIENT<br/>_MAGIC_QUERYONLINE | Web client v2 中是否启用新的在线状态查询方法; `1`:启用,`0`:不启用,默认不启用 | `0` |
| RUSTDESK_API_RUSTDESK_WS_HOST | 自定义Websocket Host | `wss://192.168.1.123:1234` |
| ----PROXY配置----- | ---------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | 是否启用代理:`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | 代理地址 | `http://127.0.0.1:1080` |
| ----JWT配置---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | 自定义JWT KEY,为空则不启用JWT<br/>如果没使用`lejianwen/rustdesk-server`中的`MUST_LOGIN`,建议设置为空 | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | `168h` |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT有效时间 | 360000 |
### 运行
@@ -261,12 +309,6 @@
#或者使用generate_api.go生成api并运行
go generate generate_api.go
```
> 注意:使用 `go run` 或编译后的二进制时,当前目录下必须存在 `conf` 和 `resources`
> 目录。如果在其他目录运行,可通过 `-c` 和环境变量
> `RUSTDESK_API_GIN_RESOURCES_PATH` 指定绝对路径,例如:
> ```bash
> RUSTDESK_API_GIN_RESOURCES_PATH=/opt/rustdesk-api/resources ./apimain -c /opt/rustdesk-api/conf/config.yaml
> ```
5. 编译,如果想自己编译,先cd到项目根目录然后windows下直接运行`build.bat`,linux下运行`build.sh`,编译后会在`release`
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
@@ -331,5 +373,3 @@
</a>
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server

View File

@@ -8,17 +8,10 @@ desktop software that provides self-hosted solutions.
<img src="https://img.shields.io/badge/gin-v1.9.0-lightBlue"/>
<img src="https://img.shields.io/badge/gorm-v1.25.7-green"/>
<img src="https://img.shields.io/badge/swag-v1.16.3-yellow"/>
<img src="https://goreportcard.com/badge/github.com/lejianwen/rustdesk-api/v2"/>
<img src="https://img.shields.io/badge/i18n-7-green"/>
<img src="https://github.com/lejianwen/rustdesk-api/actions/workflows/build.yml/badge.svg"/>
</div>
## Better used with [lejianwen/rustdesk-server].
> [lejianwen/rustdesk-server] is a fork of the official RustDesk Server repository.
> 1. Solves the API connection timeout issue.
> 2. Can enforce login before initiating a connection.
> 3. Supports client websocket.
# Features
- PC API
@@ -101,8 +94,8 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
- For `OIDC`, you must set the `Issuer`. And `Scopes` is optional which default is `openid,email,profile`, please make sure this `Oauth App` can access `sub`, `email` and `preferred_username`
- Create a `GitHub OAuth App`
at `Settings` -> `Developer settings` -> `OAuth Apps` -> `New OAuth App` [here](https://github.com/settings/developers).
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oidc/callback`,
e.g., `http://127.0.0.1:21114/api/oidc/callback`.
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oauth/callback`,
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
7. Login logs
8. Connection logs
@@ -116,6 +109,8 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
* Custom commands can be added
* Custom commands can be executed
![rustdesk_command_advance](./docs/en_img/rustdesk_command_advance.png)
11. **LDAP Support**, When you setup the LDAP(test for OpenLDAP and AD), you can login with the LDAP's user. https://github.com/lejianwen/rustdesk-api/issues/114 , if LDAP fail fallback local user
### Web Client:
@@ -125,6 +120,9 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
pre-configured.
3. After logging in, the ID server and key will be automatically synced.
4. The address book will also be automatically saved to the web client for convenient use.
5. Now supports `v2 Preview`, accessible at `/webclient2`
![webclientv2](./docs/webclientv2.png)
6. `v2 preview` deployment, [WIKI](https://github.com/lejianwen/rustdesk-api/wiki)
### Automated Documentation : API documentation is generated using Swag, making it easier for developers to understand and use the API.
@@ -147,59 +145,110 @@ displaying data.Frontend code is available at [rustdesk-api-web](https://github.
### Configuration
* [Config File](./conf/config.yaml)
* Modify the configuration in `conf/config.yaml`.
* If `gorm.type` is set to `sqlite`, MySQL-related configurations are not required.
* Language support: `en` and `zh-CN` are supported. The default is `zh-CN`.
```yaml
lang: "en"
app:
web-client: 1 # web client route 1:open 0:close
register: false #register enable
show-swagger: 0 #show swagger 1:open 0:close
gin:
api-addr: "0.0.0.0:21114"
mode: "release"
resources-path: 'resources'
trust-proxy: ""
gorm:
type: "sqlite"
max-idle-conns: 10
max-open-conns: 100
mysql:
username: "root"
password: "111111"
addr: "192.168.1.66:3308"
dbname: "rustdesk"
rustdesk:
id-server: "192.168.1.66:21116"
relay-server: "192.168.1.66:21117"
api-server: "http://192.168.1.66:21114"
key: "123456789"
personal: 1
logger:
path: "./runtime/log.txt"
level: "warn" #trace,debug,info,warn,error,fatal
report-caller: true
proxy:
enable: false
host: ""
jwt:
key: ""
expire-duration: 360000
ldap:
enable: false
url: "ldap://ldap.example.com:389"
tls: false
tls-verify: false
base-dn: "dc=example,dc=com"
bind-dn: "cn=admin,dc=example,dc=com"
bind-password: "password"
user:
base-dn: "ou=users,dc=example,dc=com"
enable-attr: "" #The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
enable-attr-value: "" # The value of the enable attribute when the user is enabled. If you are using AD, just set random value, it will be ignored.
filter: "(cn=*)"
username: "uid" # The attribute name of the user for usernamem if you are using AD, it should be "sAMAccountName"
email: "mail"
first-name: "givenName"
last-name: "sn"
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
```
### Environment Variables
The environment variables correspond one-to-one with the configurations in the `conf/config.yaml` file. The prefix for variable names is `RUSTDESK_API`.
The table below does not list all configurations. Please refer to the configurations in `conf/config.yaml`.
| Variable Name | Description | Example |
|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai |
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration | `168h` |
| RUSTDESK_API_APP_DISABLE_PWD_LOGIN | disable password login | `false` |
| RUSTDESK_API_APP_REGISTER_STATUS | register user default status ; 1 enabled , 2 disabled ; default 1 | `1` |
| RUSTDESK_API_APP_CAPTCHA_THRESHOLD | captcha threshold; -1 disabled, 0 always enable, >0 threshold ;default `3` | `3` |
| RUSTDESK_API_APP_BAN_THRESHOLD | ban ip threshold; 0 disabled, >0 threshold ; default `0` | `0` |
| ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| RUSTDESK_API_MYSQL_TLS | Whether to enable TLS, optional values: `true`, `false`, `skip-verify`, `custom` | `false` |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
| RUSTDESK_API_RUSTDESK_WS_HOST | Custom Websocket Host | `wss://192.168.1.123:1234` |
| ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
| ----JWT---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | Custom JWT KEY, if empty JWT is not enabled.<br/>If `MUST_LOGIN` from `lejianwen/rustdesk-server` is not used, it is recommended to leave it empty. | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | `168h` |
| Variable Name | Description | Example |
|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
| TZ | timezone | Asia/Shanghai |
| RUSTDESK_API_LANG | Language | `en`,`zh-CN` |
| RUSTDESK_API_APP_WEB_CLIENT | web client on/off; 1: on, 0 off, default: 1 | 1 |
| RUSTDESK_API_APP_REGISTER | register enable; `true`, `false`; default:`false` | `false` |
| RUSTDESK_API_APP_SHOW_SWAGGER | swagger visible; 1: yes, 0: no; default: 0 | `0` |
| RUSTDESK_API_APP_TOKEN_EXPIRE | token expire duration(second) | `3600` |
| ----- ADMIN Configuration----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | Admin Title | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | Admin welcome message, you can use `html` | |
| RUSTDESK_API_ADMIN_HELLO_FILE | Admin welcome message file,<br>will override `RUSTDESK_API_ADMIN_HELLO` | `./conf/admin/hello.html` |
| ----- GIN Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GIN_TRUST_PROXY | Trusted proxy IPs, separated by commas. | 192.168.1.2,192.168.1.3 |
| ----- GORM Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_GORM_TYPE | Database type (`sqlite` or `mysql`). Default is `sqlite`. | sqlite |
| RUSTDESK_API_GORM_MAX_IDLE_CONNS | Maximum idle connections | 10 |
| RUSTDESK_API_GORM_MAX_OPEN_CONNS | Maximum open connections | 100 |
| RUSTDESK_API_RUSTDESK_PERSONAL | Open Personal Api 1:Enable,0:Disable | 1 |
| ----- MYSQL Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_MYSQL_USERNAME | MySQL username | root |
| RUSTDESK_API_MYSQL_PASSWORD | MySQL password | 111111 |
| RUSTDESK_API_MYSQL_ADDR | MySQL address | 192.168.1.66:3306 |
| RUSTDESK_API_MYSQL_DBNAME | MySQL database name | rustdesk |
| ----- RUSTDESK Configuration ----- | --------------------------------------- | ----------------------------- |
| RUSTDESK_API_RUSTDESK_ID_SERVER | Rustdesk ID server address | 192.168.1.66:21116 |
| RUSTDESK_API_RUSTDESK_RELAY_SERVER | Rustdesk relay server address | 192.168.1.66:21117 |
| RUSTDESK_API_RUSTDESK_API_SERVER | Rustdesk API server address | http://192.168.1.66:21114 |
| RUSTDESK_API_RUSTDESK_KEY | Rustdesk key | 123456789 |
| RUSTDESK_API_RUSTDESK_KEY_FILE | Rustdesk key file | `./conf/data/id_ed25519.pub` |
| RUSTDESK_API_RUSTDESK<br/>_WEBCLIENT_MAGIC_QUERYONLINE | New online query method is enabled in the web client v2; '1': Enabled, '0': Disabled, not enabled by default | `0` |
| ---- PROXY ----- | --------------- | ---------- |
| RUSTDESK_API_PROXY_ENABLE | proxy_enable :`false`, `true` | `false` |
| RUSTDESK_API_PROXY_HOST | proxy_host | `http://127.0.0.1:1080` |
| ----JWT---- | -------- | -------- |
| RUSTDESK_API_JWT_KEY | Custom JWT KEY, if empty JWT is not enabled.<br/>If `MUST_LOGIN` from `lejianwen/rustdesk-server` is not used, it is recommended to leave it empty. | |
| RUSTDESK_API_JWT_EXPIRE_DURATION | JWT expire duration | 360000 |
### Installation Steps
@@ -256,17 +305,10 @@ Download the release from [release](https://github.com/lejianwen/rustdesk-api/re
4. Run:
```bash
# Run directly
go run cmd/apimain.go
# Or generate and run the API using generate_api.go
go generate generate_api.go
```
> **Note:** When using `go run` or the compiled binary, the `conf` and `resources`
> directories must exist relative to the current working directory. If you run
> the program from another location, specify absolute paths with `-c` and the
> `RUSTDESK_API_GIN_RESOURCES_PATH` environment variable. Example:
> ```bash
> RUSTDESK_API_GIN_RESOURCES_PATH=/opt/rustdesk-api/resources ./apimain -c /opt/rustdesk-api/conf/config.yaml
> ```
go run cmd/apimain.go
# Or generate and run the API using generate_api.go
go generate generate_api.go
```
5. To compile, change to the project root directory. For Windows, run `build.bat`, and for Linux, run `build.sh`. After
compiling, the corresponding executables will be generated in the `release` directory. Run the compiled executables
@@ -329,7 +371,4 @@ Thanks to everyone who contributed!
<img src="https://contrib.rocks/image?repo=lejianwen/rustdesk-api" />
</a>
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server
## Thanks for your support! If you find this project useful, please give it a ⭐️. Thank you!

View File

@@ -1,30 +1,26 @@
package main
import (
"fmt"
"Gwen/config"
"Gwen/global"
"Gwen/http"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/logger"
"Gwen/lib/orm"
"Gwen/lib/upload"
"Gwen/model"
"Gwen/service"
"Gwen/utils"
"github.com/go-redis/redis/v8"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra"
"os"
"strconv"
"time"
"github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/logger"
"github.com/lejianwen/rustdesk-api/v2/lib/orm"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra"
)
const DatabaseVersion = 265
// @title 管理系统API
// @version 1.0
// @description 接口
@@ -56,10 +52,6 @@ var resetPwdCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
pwd := args[0]
admin := service.AllService.UserService.InfoById(1)
if admin.Id == 0 {
global.Logger.Warn("user not found! ")
return
}
err := service.AllService.UserService.UpdatePassword(admin, pwd)
if err != nil {
global.Logger.Error("reset password fail! ", err)
@@ -86,10 +78,6 @@ var resetUserPwdCmd = &cobra.Command{
return
}
u := service.AllService.UserService.InfoById(uint(uid))
if u.Id == 0 {
global.Logger.Warn("user not found! ")
return
}
err = service.AllService.UserService.UpdatePassword(u, pwd)
if err != nil {
global.Logger.Warn("reset password fail! ", err)
@@ -144,42 +132,20 @@ func InitGlobal() {
}
//gorm
if global.Config.Gorm.Type == config.TypeMysql {
dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
global.Config.Mysql.Username,
global.Config.Mysql.Password,
global.Config.Mysql.Addr,
global.Config.Mysql.Dbname,
global.Config.Mysql.Tls,
)
dns := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/" + global.Config.Mysql.Dbname + "?charset=utf8mb4&parseTime=True&loc=Local"
global.DB = orm.NewMysql(&orm.MysqlConfig{
Dsn: dsn,
Dns: dns,
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
}, global.Logger)
} else if global.Config.Gorm.Type == config.TypePostgresql {
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
global.Config.Postgresql.Host,
global.Config.Postgresql.Port,
global.Config.Postgresql.User,
global.Config.Postgresql.Password,
global.Config.Postgresql.Dbname,
global.Config.Postgresql.Sslmode,
global.Config.Postgresql.TimeZone,
)
global.DB = orm.NewPostgresql(&orm.PostgresqlConfig{
Dsn: dsn,
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
}, global.Logger)
})
} else {
//sqlite
global.DB = orm.NewSqlite(&orm.SqliteConfig{
MaxIdleConns: global.Config.Gorm.MaxIdleConns,
MaxOpenConns: global.Config.Gorm.MaxOpenConns,
}, global.Logger)
})
}
DatabaseAutoUpdate()
//validator
global.ApiInitValidator()
@@ -196,25 +162,13 @@ func InitGlobal() {
//jwt
//fmt.Println(global.Config.Jwt.PrivateKey)
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration)
global.Jwt = jwt.NewJwt(global.Config.Jwt.Key, global.Config.Jwt.ExpireDuration*time.Second)
//locker
global.Lock = lock.NewLocal()
//service
service.New(&global.Config, global.DB, global.Logger, global.Jwt, global.Lock)
global.LoginLimiter = utils.NewLoginLimiter(utils.SecurityPolicy{
CaptchaThreshold: global.Config.App.CaptchaThreshold,
BanThreshold: global.Config.App.BanThreshold,
AttemptsWindow: 10 * time.Minute,
BanDuration: 30 * time.Minute,
})
global.LoginLimiter.RegisterProvider(utils.B64StringCaptchaProvider{})
DatabaseAutoUpdate()
}
func DatabaseAutoUpdate() {
version := DatabaseVersion
version := 260
db := global.DB
@@ -224,26 +178,20 @@ func DatabaseAutoUpdate() {
if dbName == "" {
dbName = global.Config.Mysql.Dbname
// 移除 DSN 中的数据库名称,以便初始连接时不指定数据库
dsnWithoutDB := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
global.Config.Mysql.Username,
global.Config.Mysql.Password,
global.Config.Mysql.Addr,
"",
)
dsnWithoutDB := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/?charset=utf8mb4&parseTime=True&loc=Local"
//新链接
dbWithoutDB := orm.NewMysql(&orm.MysqlConfig{
Dsn: dsnWithoutDB,
}, global.Logger)
Dns: dsnWithoutDB,
})
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
sqlDBWithoutDB, err := dbWithoutDB.DB()
if err != nil {
global.Logger.Errorf("获取底层 *sql.DB 对象失败: %v", err)
global.Logger.Error("获取底层 *sql.DB 对象失败: %v\n", err)
return
}
defer func() {
if err := sqlDBWithoutDB.Close(); err != nil {
global.Logger.Errorf("关闭连接失败: %v", err)
global.Logger.Error("关闭连接失败: %v\n", err)
}
}()
@@ -264,7 +212,6 @@ func DatabaseAutoUpdate() {
if v.Version < uint(version) {
Migrate(uint(version))
}
// 245迁移
if v.Version < 245 {
//oauths 表的 oauth_type 字段设置为 op同样的值
@@ -287,7 +234,7 @@ func DatabaseAutoUpdate() {
}
func Migrate(version uint) {
global.Logger.Info("Migrating....", version)
global.Logger.Info("migrating....", version)
err := global.DB.AutoMigrate(
&model.Version{},
&model.User{},
@@ -305,7 +252,6 @@ func Migrate(version uint) {
&model.AddressBookCollection{},
&model.AddressBookCollectionRule{},
&model.ServerCmd{},
&model.DeviceGroup{},
)
if err != nil {
global.Logger.Error("migrate err :=>", err)
@@ -346,11 +292,7 @@ func Migrate(version uint) {
// 生成随机密码
pwd := utils.RandomString(8)
global.Logger.Info("Admin Password Is: ", pwd)
var err error
admin.Password, err = utils.EncryptPassword(pwd)
if err != nil {
global.Logger.Fatalf("failed to generate admin password: %v", err)
}
admin.Password = service.AllService.UserService.EncryptPassword(pwd)
global.DB.Create(admin)
}

View File

@@ -1 +1 @@
### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk API](https://github.com/lejianwen/rustdesk-api)
### 👏👏👏 你好 ***{{username}}*** 欢迎使用 [RustDesk Api](https://github.com/lejianwen/rustdesk-api)

View File

@@ -2,21 +2,13 @@ lang: "zh-CN"
app:
web-client: 1 # 1:启用 0:禁用
register: false #是否开启注册
register-status: 1 # 注册用户默认状态 1:启用 2:禁用
captcha-threshold: 3 # <0:disabled, 0 always, >0:enabled
ban-threshold: 0 # 0:disabled, >0:enabled
show-swagger: 0 # 1:启用 0:禁用
token-expire: 168h
token-expire: 360000
web-sso: true #web auth sso
disable-pwd-login: false #禁用密码登录
admin:
title: "RustDesk API Admin"
title: "RustDesk Api Admin"
hello-file: "./conf/admin/hello.html" #优先使用file
hello: ""
# ID Server and Relay Server ports https://github.com/lejianwen/rustdesk-api/issues/257
id-server-port: 21116 # ID Server port (for server cmd)
relay-server-port: 21117 # ID Server port (for server cmd)
gin:
api-addr: "0.0.0.0:21114"
mode: "release" #release,debug,test
@@ -31,17 +23,6 @@ mysql:
password: ""
addr: ""
dbname: ""
tls: "false" # true / false / skip-verify / custom
postgresql:
host: "127.0.0.1"
port: "5432"
user: ""
password: ""
dbname: "postgres"
sslmode: "disable" # disable, require, verify-ca, verify-full
time-zone: "Asia/Shanghai" # Time zone for PostgreSQL connection
rustdesk:
id-server: "192.168.1.66:21116"
relay-server: "192.168.1.66:21117"
@@ -50,7 +31,6 @@ rustdesk:
key-file: "/data/id_ed25519.pub"
personal: 1
webclient-magic-queryonline: 0
ws-host: "" #eg: wss://192.168.1.3:4443
logger:
path: "./runtime/log.txt"
level: "info" #trace,debug,info,warn,error,fatal
@@ -60,11 +40,11 @@ proxy:
host: "http://127.0.0.1:1080"
jwt:
key: ""
expire-duration: 168h
expire-duration: 360000
ldap:
enable: false
url: "ldap://ldap.example.com:389"
tls-ca-file: ""
tls: false
tls-verify: false
base-dn: "dc=example,dc=com"
bind-dn: "cn=admin,dc=example,dc=com"
@@ -81,4 +61,22 @@ ldap:
last-name: "sn"
sync: false # If true, the user will be synchronized to the database when the user logs in. If false, the user will be synchronized to the database when the user be created.
admin-group: "cn=admin,dc=example,dc=com" # The group name of the admin group, if the user is in this group, the user will be an admin.
allow-group: "cn=users,dc=example,dc=com" # The group name of the users group, if the user is in this group, the user will be an login.
redis:
addr: "127.0.0.1:6379"
password: ""
db: 0
cache:
type: "file"
file-dir: "./runtime/cache"
redis-addr: "127.0.0.1:6379"
redis-pwd: ""
redis-db: 0
oss:
access-key-id: ""
access-key-secret: ""
host: ""
callback-url: ""
expire-time: 30
max-byte: 10240

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/spf13/viper"
"strings"
"time"
)
const (
@@ -14,48 +13,32 @@ const (
)
type App struct {
WebClient int `mapstructure:"web-client"`
Register bool `mapstructure:"register"`
RegisterStatus int `mapstructure:"register-status"`
ShowSwagger int `mapstructure:"show-swagger"`
TokenExpire time.Duration `mapstructure:"token-expire"`
WebSso bool `mapstructure:"web-sso"`
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
CaptchaThreshold int `mapstructure:"captcha-threshold"`
BanThreshold int `mapstructure:"ban-threshold"`
WebClient int `mapstructure:"web-client"`
Register bool `mapstructure:"register"`
ShowSwagger int `mapstructure:"show-swagger"`
TokenExpire int `mapstructure:"token-expire"`
WebSso bool `mapstructure:"web-sso"`
}
type Admin struct {
Title string `mapstructure:"title"`
Hello string `mapstructure:"hello"`
HelloFile string `mapstructure:"hello-file"`
IdServerPort int `mapstructure:"id-server-port"`
RelayServerPort int `mapstructure:"relay-server-port"`
Title string `mapstructure:"title"`
Hello string `mapstructure:"hello"`
HelloFile string `mapstructure:"hello-file"`
}
type Config struct {
Lang string `mapstructure:"lang"`
App App
Admin Admin
Gorm Gorm
Mysql Mysql
Postgresql Postgresql
Gin Gin
Logger Logger
Redis Redis
Cache Cache
Oss Oss
Jwt Jwt
Rustdesk Rustdesk
Proxy Proxy
Ldap Ldap
}
func (a *Admin) Init() {
if a.IdServerPort == 0 {
a.IdServerPort = DefaultIdServerPort
}
if a.RelayServerPort == 0 {
a.RelayServerPort = DefaultRelayServerPort
}
Lang string `mapstructure:"lang"`
App App
Admin Admin
Gorm Gorm
Mysql Mysql
Gin Gin
Logger Logger
Redis Redis
Cache Cache
Oss Oss
Jwt Jwt
Rustdesk Rustdesk
Proxy Proxy
Ldap Ldap
}
// Init 初始化配置
@@ -89,10 +72,10 @@ func Init(rowVal *Config, path string) *viper.Viper {
})
*/
if err := v.Unmarshal(rowVal); err != nil {
panic(fmt.Errorf("Fatal error config: %s \n", err))
fmt.Println(err)
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Admin.Init()
rowVal.Rustdesk.ParsePort()
return v
}

View File

@@ -1,9 +1,8 @@
package config
const (
TypeSqlite = "sqlite"
TypeMysql = "mysql"
TypePostgresql = "postgresql"
TypeSqlite = "sqlite"
TypeMysql = "mysql"
)
type Gorm struct {
@@ -17,15 +16,4 @@ type Mysql struct {
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Dbname string `mapstructure:"dbname"`
Tls string `mapstructure:"tls"` // true / false / skip-verify / custom
}
type Postgresql struct {
Host string `mapstructure:"host"`
Port string `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Dbname string `mapstructure:"dbname"`
Sslmode string `mapstructure:"sslmode"` // "disable", "require", "verify-ca", "verify-full"
TimeZone string `mapstructure:"time-zone"` // e.g., "Asia/Shanghai"
}

View File

@@ -1,17 +1,16 @@
package config
type LdapUser struct {
BaseDn string `mapstructure:"base-dn"` // The base DN of the user for searching
EnableAttr string `mapstructure:"enable-attr"` // The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
EnableAttrValue string `mapstructure:"enable-attr-value"` // The value of the enable attribute when the user is enabled. If you are using AD, just leave it random str, it will be ignored.
Filter string `mapstructure:"filter"`
Username string `mapstructure:"username"`
Email string `mapstructure:"email"`
FirstName string `mapstructure:"first-name"`
LastName string `mapstructure:"last-name"`
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
AllowGroup string `mapstructure:"allow-group"` // Which group is allowed to login
BaseDn string `mapstructure:"base-dn"` // The base DN of the user for searching
EnableAttr string `mapstructure:"enable-attr"` // The attribute name of the user for enabling, in AD it is "userAccountControl", empty means no enable attribute, all users are enabled
EnableAttrValue string `mapstructure:"enable-attr-value"` // The value of the enable attribute when the user is enabled. If you are using AD, just leave it random str, it will be ignored.
Filter string `mapstructure:"filter"`
Username string `mapstructure:"username"`
Email string `mapstructure:"email"`
FirstName string `mapstructure:"first-name"`
LastName string `mapstructure:"last-name"`
Sync bool `mapstructure:"sync"` // Will sync the user's information to the internal database
AdminGroup string `mapstructure:"admin-group"` // Which group is the admin group
}
// type LdapGroup struct {
@@ -25,13 +24,13 @@ type LdapUser struct {
// }
type Ldap struct {
Enable bool `mapstructure:"enable"`
Url string `mapstructure:"url"`
TlsCaFile string `mapstructure:"tls-ca-file"`
TlsVerify bool `mapstructure:"tls-verify"`
BaseDn string `mapstructure:"base-dn"`
BindDn string `mapstructure:"bind-dn"`
BindPassword string `mapstructure:"bind-password"`
User LdapUser `mapstructure:"user"`
Enable bool `mapstructure:"enable"`
Url string `mapstructure:"url"`
TLS bool `mapstructure:"tls"`
TlsVerify bool `mapstructure:"tls-verify"`
BaseDn string `mapstructure:"base-dn"`
BindDn string `mapstructure:"bind-dn"`
BindPassword string `mapstructure:"bind-password"`
User LdapUser `mapstructure:"user"`
// Group LdapGroup `mapstructure:"group"`
}
}

View File

@@ -3,20 +3,18 @@ package config
type GithubOauth struct {
ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"`
RedirectUrl string `mapstructure:"redirect-url"`
}
type GoogleOauth struct {
ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"`
RedirectUrl string `mapstructure:"redirect-url"`
}
type OidcOauth struct {
Issuer string `mapstructure:"issuer"`
ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"`
}
type LinuxdoOauth struct {
ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"`
}
RedirectUrl string `mapstructure:"redirect-url"`
}

View File

@@ -2,6 +2,8 @@ package config
import (
"os"
"strconv"
"strings"
)
const (
@@ -19,8 +21,7 @@ type Rustdesk struct {
KeyFile string `mapstructure:"key-file"`
Personal int `mapstructure:"personal"`
//webclient-magic-queryonline
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
WsHost string `mapstructure:"ws-host"`
WebclientMagicQueryonline int `mapstructure:"webclient-magic-queryonline"`
}
func (rd *Rustdesk) LoadKeyFile() {
@@ -38,3 +39,19 @@ func (rd *Rustdesk) LoadKeyFile() {
return
}
}
func (rd *Rustdesk) ParsePort() {
// Parse port
idres := strings.Split(rd.IdServer, ":")
if len(idres) == 1 {
rd.IdServerPort = DefaultIdServerPort
} else if len(idres) == 2 {
rd.IdServerPort, _ = strconv.Atoi(idres[1])
}
relayres := strings.Split(rd.RelayServer, ":")
if len(relayres) == 1 {
rd.RelayServerPort = DefaultRelayServerPort
} else if len(relayres) == 2 {
rd.RelayServerPort, _ = strconv.Atoi(relayres[1])
}
}

View File

View File

@@ -5,7 +5,7 @@ services:
dockerfile: Dockerfile.dev
args:
COUNTRY: CN
FRONTEND_GIT_REPO: https://github.com/lejianwen/rustdesk-api-web.git
FREONTEND_GIT_REPO: https://github.com/lejianwen/rustdesk-api-web.git
FRONTEND_GIT_BRANCH: master
# image: lejianwen/rustdesk-api
container_name: rustdesk-api
@@ -21,4 +21,4 @@ services:
- ./data/rustdesk/api:/app/data #将数据库挂载出来方便备份
- ./conf:/app/conf # config
# - ./resources:/app/resources # 静态资源
restart: unless-stopped
restart: unless-stopped

View File

@@ -1407,280 +1407,6 @@ const docTemplateadmin = `{
}
}
},
"/admin/device_group/create": {
"post": {
"security": [
{
"token": []
}
],
"description": "创建设备群组",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "创建设备群组",
"parameters": [
{
"description": "设备群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.DeviceGroup"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "设备群组删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组删除",
"parameters": [
{
"description": "群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/detail/{id}": {
"get": {
"security": [
{
"token": []
}
],
"description": "设备群组详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组详情",
"parameters": [
{
"type": "integer",
"description": "ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.Group"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "群组列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"群组"
],
"summary": "群组列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.GroupList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/update": {
"post": {
"security": [
{
"token": []
}
],
"description": "设备群组编辑",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组编辑",
"parameters": [
{
"description": "群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.Group"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/file/oss_token": {
"get": {
"security": [
@@ -2057,7 +1783,7 @@ const docTemplateadmin = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
"$ref": "#/definitions/Gwen_http_request_admin.Login"
}
}
],
@@ -5378,6 +5104,27 @@ const docTemplateadmin = `{
}
},
"definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": {
"type": "object",
"required": [
@@ -5493,20 +5240,6 @@ const docTemplateadmin = `{
}
}
},
"admin.DeviceGroupForm": {
"type": "object",
"required": [
"name"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"admin.GroupForm": {
"type": "object",
"required": [
@@ -5569,7 +5302,8 @@ const docTemplateadmin = `{
"required": [
"client_id",
"client_secret",
"oauth_type"
"oauth_type",
"redirect_url"
],
"properties": {
"auto_register": {
@@ -5593,10 +5327,7 @@ const docTemplateadmin = `{
"op": {
"type": "string"
},
"pkce_enable": {
"type": "boolean"
},
"pkce_method": {
"redirect_url": {
"type": "string"
},
"scopes": {
@@ -5624,9 +5355,6 @@ const docTemplateadmin = `{
"cpu": {
"type": "string"
},
"group_id": {
"type": "integer"
},
"hostname": {
"type": "string"
},
@@ -5814,30 +5542,6 @@ const docTemplateadmin = `{
}
}
},
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"captcha_id": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {
@@ -6138,23 +5842,6 @@ const docTemplateadmin = `{
}
}
},
"model.DeviceGroup": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.Group": {
"type": "object",
"properties": {
@@ -6286,10 +5973,7 @@ const docTemplateadmin = `{
"op": {
"type": "string"
},
"pkce_enable": {
"type": "boolean"
},
"pkce_method": {
"redirect_url": {
"type": "string"
},
"scopes": {
@@ -6329,9 +6013,6 @@ const docTemplateadmin = `{
"created_at": {
"type": "string"
},
"group_id": {
"type": "integer"
},
"hostname": {
"type": "string"
},

View File

@@ -1400,280 +1400,6 @@
}
}
},
"/admin/device_group/create": {
"post": {
"security": [
{
"token": []
}
],
"description": "创建设备群组",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "创建设备群组",
"parameters": [
{
"description": "设备群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.DeviceGroup"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/delete": {
"post": {
"security": [
{
"token": []
}
],
"description": "设备群组删除",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组删除",
"parameters": [
{
"description": "群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/detail/{id}": {
"get": {
"security": [
{
"token": []
}
],
"description": "设备群组详情",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组详情",
"parameters": [
{
"type": "integer",
"description": "ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.Group"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/list": {
"get": {
"security": [
{
"token": []
}
],
"description": "群组列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"群组"
],
"summary": "群组列表",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "页大小",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.GroupList"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/device_group/update": {
"post": {
"security": [
{
"token": []
}
],
"description": "设备群组编辑",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"设备群组"
],
"summary": "设备群组编辑",
"parameters": [
{
"description": "群组信息",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.DeviceGroupForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/model.Group"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/admin/file/oss_token": {
"get": {
"security": [
@@ -2050,7 +1776,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login"
"$ref": "#/definitions/Gwen_http_request_admin.Login"
}
}
],
@@ -5371,6 +5097,27 @@
}
},
"definitions": {
"Gwen_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"admin.AddressBookForm": {
"type": "object",
"required": [
@@ -5486,20 +5233,6 @@
}
}
},
"admin.DeviceGroupForm": {
"type": "object",
"required": [
"name"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"admin.GroupForm": {
"type": "object",
"required": [
@@ -5562,7 +5295,8 @@
"required": [
"client_id",
"client_secret",
"oauth_type"
"oauth_type",
"redirect_url"
],
"properties": {
"auto_register": {
@@ -5586,10 +5320,7 @@
"op": {
"type": "string"
},
"pkce_enable": {
"type": "boolean"
},
"pkce_method": {
"redirect_url": {
"type": "string"
},
"scopes": {
@@ -5617,9 +5348,6 @@
"cpu": {
"type": "string"
},
"group_id": {
"type": "integer"
},
"hostname": {
"type": "string"
},
@@ -5807,30 +5535,6 @@
}
}
},
"github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login": {
"type": "object",
"required": [
"password",
"username"
],
"properties": {
"captcha": {
"type": "string"
},
"captcha_id": {
"type": "string"
},
"password": {
"type": "string"
},
"platform": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"model.AddressBook": {
"type": "object",
"properties": {
@@ -6131,23 +5835,6 @@
}
}
},
"model.DeviceGroup": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"model.Group": {
"type": "object",
"properties": {
@@ -6279,10 +5966,7 @@
"op": {
"type": "string"
},
"pkce_enable": {
"type": "boolean"
},
"pkce_method": {
"redirect_url": {
"type": "string"
},
"scopes": {
@@ -6322,9 +6006,6 @@
"created_at": {
"type": "string"
},
"group_id": {
"type": "integer"
},
"hostname": {
"type": "string"
},
@@ -6588,4 +6269,4 @@
"in": "header"
}
}
}
}

View File

@@ -1,5 +1,19 @@
basePath: /api
definitions:
Gwen_http_request_admin.Login:
properties:
captcha:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
admin.AddressBookForm:
properties:
alias:
@@ -77,15 +91,6 @@ definitions:
- new_password
- old_password
type: object
admin.DeviceGroupForm:
properties:
id:
type: integer
name:
type: string
required:
- name
type: object
admin.GroupForm:
properties:
id:
@@ -139,9 +144,7 @@ definitions:
type: string
op:
type: string
pkce_enable:
type: boolean
pkce_method:
redirect_url:
type: string
scopes:
type: string
@@ -149,6 +152,7 @@ definitions:
- client_id
- client_secret
- oauth_type
- redirect_url
type: object
admin.PeerBatchDeleteForm:
properties:
@@ -163,8 +167,6 @@ definitions:
properties:
cpu:
type: string
group_id:
type: integer
hostname:
type: string
id:
@@ -290,22 +292,6 @@ definitions:
required:
- ids
type: object
github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login:
properties:
captcha:
type: string
captcha_id:
type: string
password:
type: string
platform:
type: string
username:
type: string
required:
- password
- username
type: object
model.AddressBook:
properties:
alias:
@@ -506,17 +492,6 @@ definitions:
total:
type: integer
type: object
model.DeviceGroup:
properties:
created_at:
type: string
id:
type: integer
name:
type: string
updated_at:
type: string
type: object
model.Group:
properties:
created_at:
@@ -604,9 +579,7 @@ definitions:
type: string
op:
type: string
pkce_enable:
type: boolean
pkce_method:
redirect_url:
type: string
scopes:
type: string
@@ -632,8 +605,6 @@ definitions:
type: string
created_at:
type: string
group_id:
type: integer
hostname:
type: string
id:
@@ -1639,167 +1610,6 @@ paths:
summary: RUSTDESK服务配置
tags:
- ADMIN
/admin/device_group/create:
post:
consumes:
- application/json
description: 创建设备群组
parameters:
- description: 设备群组信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.DeviceGroupForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.DeviceGroup'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 创建设备群组
tags:
- 设备群组
/admin/device_group/delete:
post:
consumes:
- application/json
description: 设备群组删除
parameters:
- description: 群组信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.DeviceGroupForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备群组删除
tags:
- 设备群组
/admin/device_group/detail/{id}:
get:
consumes:
- application/json
description: 设备群组详情
parameters:
- description: ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.Group'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备群组详情
tags:
- 设备群组
/admin/device_group/list:
get:
consumes:
- application/json
description: 群组列表
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 页大小
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.GroupList'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 群组列表
tags:
- 群组
/admin/device_group/update:
post:
consumes:
- application/json
description: 设备群组编辑
parameters:
- description: 群组信息
in: body
name: body
required: true
schema:
$ref: '#/definitions/admin.DeviceGroupForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.Group'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- token: []
summary: 设备群组编辑
tags:
- 设备群组
/admin/file/oss_token:
get:
consumes:
@@ -2020,7 +1830,7 @@ paths:
name: body
required: true
schema:
$ref: '#/definitions/github_com_lejianwen_rustdesk-api_v2_http_request_admin.Login'
$ref: '#/definitions/Gwen_http_request_admin.Login'
produces:
- application/json
responses:

View File

@@ -767,66 +767,6 @@ const docTemplateapi = `{
}
}
},
"/device-group/accessible": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "机器",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"群组"
],
"summary": "设备",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
},
{
"type": "integer",
"description": "状态",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "accessible",
"name": "accessible",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.DataResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -954,6 +894,35 @@ const docTemplateapi = `{
}
}
},
"/oauth/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/oidc/auth": {
"post": {
"description": "OidcAuth",
@@ -1012,35 +981,6 @@ const docTemplateapi = `{
}
}
},
"/oidc/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/peers": {
"get": {
"security": [
@@ -1208,7 +1148,7 @@ const docTemplateapi = `{
"application/json"
],
"tags": [
"System"
"地址"
],
"summary": "提交系统信息",
"parameters": [
@@ -1238,35 +1178,6 @@ const docTemplateapi = `{
}
}
},
"/sysinfo_ver": {
"post": {
"description": "获取系统版本信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"System"
],
"summary": "获取系统版本信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/users": {
"get": {
"security": [

View File

@@ -760,66 +760,6 @@
}
}
},
"/device-group/accessible": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "机器",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"群组"
],
"summary": "设备",
"parameters": [
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "pageSize",
"in": "query"
},
{
"type": "integer",
"description": "状态",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "accessible",
"name": "accessible",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.DataResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/heartbeat": {
"post": {
"description": "心跳",
@@ -947,6 +887,35 @@
}
}
},
"/oauth/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/oidc/auth": {
"post": {
"description": "OidcAuth",
@@ -1005,35 +974,6 @@
}
}
},
"/oidc/callback": {
"get": {
"description": "OauthCallback",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Oauth"
],
"summary": "OauthCallback",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.LoginRes"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/peers": {
"get": {
"security": [
@@ -1201,7 +1141,7 @@
"application/json"
],
"tags": [
"System"
"地址"
],
"summary": "提交系统信息",
"parameters": [
@@ -1231,35 +1171,6 @@
}
}
},
"/sysinfo_ver": {
"post": {
"description": "获取系统版本信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"System"
],
"summary": "获取系统版本信息",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.ErrorResponse"
}
}
}
}
},
"/users": {
"get": {
"security": [

View File

@@ -671,44 +671,6 @@ paths:
summary: 用户信息
tags:
- 用户
/device-group/accessible:
get:
consumes:
- application/json
description: 机器
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: pageSize
type: integer
- description: 状态
in: query
name: status
type: integer
- description: accessible
in: query
name: accessible
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.DataResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- BearerAuth: []
summary: 设备
tags:
- 群组
/heartbeat:
post:
consumes:
@@ -792,6 +754,25 @@ paths:
summary: 登出
tags:
- 登录
/oauth/callback:
get:
consumes:
- application/json
description: OauthCallback
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.LoginRes'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: OauthCallback
tags:
- Oauth
/oidc/auth:
post:
consumes:
@@ -830,25 +811,6 @@ paths:
summary: OidcAuthQuery
tags:
- Oauth
/oidc/callback:
get:
consumes:
- application/json
description: OauthCallback
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.LoginRes'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: OauthCallback
tags:
- Oauth
/peers:
get:
consumes:
@@ -973,26 +935,7 @@ paths:
$ref: '#/definitions/response.ErrorResponse'
summary: 提交系统信息
tags:
- System
/sysinfo_ver:
post:
consumes:
- application/json
description: 获取系统版本信息
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.ErrorResponse'
summary: 获取系统版本信息
tags:
- System
- 地址
/users:
get:
consumes:

View File

@@ -1,4 +1,4 @@
package main
package Gwen
//go:generate swag init -g cmd/apimain.go --output docs/api --instanceName api --exclude http/controller/admin
//go:generate swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude http/controller/api

View File

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

View File

@@ -14,7 +14,6 @@ import (
en_translations "github.com/go-playground/validator/v10/translations/en"
es_translations "github.com/go-playground/validator/v10/translations/es"
fr_translations "github.com/go-playground/validator/v10/translations/fr"
ko_translations "github.com/go-playground/validator/v10/translations/ko"
ru_translations "github.com/go-playground/validator/v10/translations/ru"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw"
@@ -52,7 +51,8 @@ func ApiInitValidator() {
panic(err)
}
err = ko_translations.RegisterDefaultTranslations(validate, koTrans)
//validate没有ko的翻译使用zh的翻译
err = zh_translations.RegisterDefaultTranslations(validate, koTrans)
if err != nil {
panic(err)
}

View File

@@ -1,16 +1,15 @@
package global
import (
"Gwen/config"
"Gwen/lib/cache"
"Gwen/lib/jwt"
"Gwen/lib/lock"
"Gwen/lib/upload"
"github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/lib/cache"
"github.com/lejianwen/rustdesk-api/v2/lib/jwt"
"github.com/lejianwen/rustdesk-api/v2/lib/lock"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
@@ -32,9 +31,8 @@ var (
ValidStruct func(*gin.Context, interface{}) []string
ValidVar func(ctx *gin.Context, field interface{}, tag string) []string
}
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Localizer func(lang string) *i18n.Localizer
LoginLimiter *utils.LoginLimiter
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Localizer func(lang string) *i18n.Localizer
)

View File

@@ -15,6 +15,7 @@ func InitI18n() {
fileInfos, err := os.ReadDir(dir)
if err != nil {
panic(err)
return
}
for _, fileInfo := range fileInfos {
//如果文件名不是.toml结尾

38
go.mod
View File

@@ -1,23 +1,19 @@
module github.com/lejianwen/rustdesk-api/v2
module Gwen
go 1.23
toolchain go1.23.10
go 1.22
require (
github.com/BurntSushi/toml v1.3.2
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/coreos/go-oidc/v3 v3.12.0
github.com/fsnotify/fsnotify v1.5.1
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-gonic/gin v1.9.0
github.com/go-ldap/ldap/v3 v3.4.10
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.26.0
github.com/go-playground/validator/v10 v10.11.2
github.com/go-redis/redis/v8 v8.11.4
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/mojocn/base64Captcha v1.3.6
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.8.1
@@ -25,13 +21,11 @@ require (
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.3
golang.org/x/crypto v0.33.0
golang.org/x/oauth2 v0.23.0
golang.org/x/text v0.22.0
golang.org/x/text v0.21.0
gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.6.0
gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.10
gorm.io/gorm v1.25.7
)
require (
@@ -43,11 +37,9 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-ldap/ldap/v3 v3.4.10 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
@@ -57,16 +49,12 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
@@ -74,9 +62,9 @@ require (
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mojocn/base64Captcha v1.3.6 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -85,11 +73,11 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@@ -1,13 +1,13 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
_ "encoding/json"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)
@@ -120,7 +120,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionR
//check to_id
if t.Type == model.ShareAddressBookRuleTypePersonal {
if t.ToId == t.UserId {
return "CannotShareToSelf", false
return "ParamsError", false
}
tou := service.AllService.UserService.InfoById(t.ToId)
if tou.Id == 0 {
@@ -135,7 +135,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(t *model.AddressBookCollectionR
return "ParamsError", false
}
// 重复检查
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
if t.Id == 0 && ex.Id > 0 {
return "ItemExists", false
}

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,11 +1,10 @@
package admin
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"os"
"strings"
)
@@ -61,30 +60,13 @@ func (co *Config) AppConfig(c *gin.Context) {
// @Security token
func (co *Config) AdminConfig(c *gin.Context) {
u := &model.User{}
token := c.GetHeader("api-token")
if token != "" {
u, _ = service.AllService.UserService.InfoByAccessToken(token)
if !service.AllService.UserService.CheckUserEnable(u) {
u.Id = 0
}
}
if u.Id == 0 {
response.Success(c, &gin.H{
"title": global.Config.Admin.Title,
})
return
}
u := service.AllService.UserService.CurUser(c)
hello := global.Config.Admin.Hello
if hello == "" {
helloFile := global.Config.Admin.HelloFile
if helloFile != "" {
b, err := os.ReadFile(helloFile)
if err == nil && len(b) > 0 {
hello = string(b)
}
helloFile := global.Config.Admin.HelloFile
if helloFile != "" {
b, err := os.ReadFile(helloFile)
if err == nil && len(b) > 0 {
hello = string(b)
}
}

View File

@@ -1,160 +0,0 @@
package admin
import (
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"strconv"
)
type DeviceGroup struct {
}
// Detail 设备群组
// @Tags 设备群组
// @Summary 设备群组详情
// @Description 设备群组详情
// @Accept json
// @Produce json
// @Param id path int true "ID"
// @Success 200 {object} response.Response{data=model.Group}
// @Failure 500 {object} response.Response
// @Router /admin/device_group/detail/{id} [get]
// @Security token
func (ct *DeviceGroup) Detail(c *gin.Context) {
id := c.Param("id")
iid, _ := strconv.Atoi(id)
u := service.AllService.GroupService.DeviceGroupInfoById(uint(iid))
if u.Id > 0 {
response.Success(c, u)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
return
}
// Create 创建设备群组
// @Tags 设备群组
// @Summary 创建设备群组
// @Description 创建设备群组
// @Accept json
// @Produce json
// @Param body body admin.DeviceGroupForm true "设备群组信息"
// @Success 200 {object} response.Response{data=model.DeviceGroup}
// @Failure 500 {object} response.Response
// @Router /admin/device_group/create [post]
// @Security token
func (ct *DeviceGroup) Create(c *gin.Context) {
f := &admin.DeviceGroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := f.ToDeviceGroup()
err := service.AllService.GroupService.DeviceGroupCreate(u)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// List 列表
// @Tags 群组
// @Summary 群组列表
// @Description 群组列表
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param page_size query int false "页大小"
// @Success 200 {object} response.Response{data=model.GroupList}
// @Failure 500 {object} response.Response
// @Router /admin/device_group/list [get]
// @Security token
func (ct *DeviceGroup) List(c *gin.Context) {
query := &admin.PageQuery{}
if err := c.ShouldBindQuery(query); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
res := service.AllService.GroupService.DeviceGroupList(query.Page, query.PageSize, nil)
response.Success(c, res)
}
// Update 编辑
// @Tags 设备群组
// @Summary 设备群组编辑
// @Description 设备群组编辑
// @Accept json
// @Produce json
// @Param body body admin.DeviceGroupForm true "群组信息"
// @Success 200 {object} response.Response{data=model.Group}
// @Failure 500 {object} response.Response
// @Router /admin/device_group/update [post]
// @Security token
func (ct *DeviceGroup) Update(c *gin.Context) {
f := &admin.DeviceGroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if f.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := f.ToDeviceGroup()
err := service.AllService.GroupService.DeviceGroupUpdate(u)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Success(c, nil)
}
// Delete 删除
// @Tags 设备群组
// @Summary 设备群组删除
// @Description 设备群组删除
// @Accept json
// @Produce json
// @Param body body admin.DeviceGroupForm true "群组信息"
// @Success 200 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /admin/device_group/delete [post]
// @Security token
func (ct *DeviceGroup) Delete(c *gin.Context) {
f := &admin.DeviceGroupForm{}
if err := c.ShouldBindJSON(f); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
id := f.Id
errList := global.Validator.ValidVar(c, id, "required,gt=0")
if len(errList) > 0 {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.GroupService.DeviceGroupInfoById(f.Id)
if u.Id > 0 {
err := service.AllService.GroupService.DeviceGroupDelete(u)
if err == nil {
response.Success(c, nil)
return
}
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed")+err.Error())
return
}
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
}

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/lib/upload"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/lib/upload"
"os"
"time"
)
@@ -38,7 +38,7 @@ func (f *File) Notify(c *gin.Context) {
res := global.Oss.Verify(c.Request)
if !res {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
response.Fail(c, 101, "权限错误")
return
}
fm := &FileBack{}

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"strconv"
)

View File

@@ -1,22 +1,145 @@
package admin
import (
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/request/admin"
apiReq "Gwen/http/request/api"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/mojocn/base64Captcha"
"sync"
"time"
)
type Login struct {
}
// Captcha 验证码结构
type Captcha struct {
Id string `json:"id"` // 验证码 ID
B64 string `json:"b64"` // base64 验证码
Code string `json:"-"` // 验证码内容
ExpiresAt time.Time `json:"-"` // 过期时间
}
type LoginLimiter struct {
mu sync.RWMutex
failCount map[string]int // 记录每个 IP 的失败次数
timestamp map[string]time.Time // 记录每个 IP 的最后失败时间
captchas map[string]Captcha // 每个 IP 的验证码
threshold int // 失败阈值
expiry time.Duration // 失败记录过期时间
}
func NewLoginLimiter(threshold int, expiry time.Duration) *LoginLimiter {
return &LoginLimiter{
failCount: make(map[string]int),
timestamp: make(map[string]time.Time),
captchas: make(map[string]Captcha),
threshold: threshold,
expiry: expiry,
}
}
// RecordFailure 记录登录失败
func (l *LoginLimiter) RecordFailure(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
// 如果该 IP 的记录已经过期,重置计数
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) > l.expiry {
l.failCount[ip] = 0
}
// 更新失败次数和时间戳
l.failCount[ip]++
l.timestamp[ip] = time.Now()
}
// NeedsCaptcha 检查是否需要验证码
func (l *LoginLimiter) NeedsCaptcha(ip string) bool {
l.mu.RLock()
defer l.mu.RUnlock()
// 检查记录是否存在且未过期
if lastTime, exists := l.timestamp[ip]; exists && time.Since(lastTime) <= l.expiry {
return l.failCount[ip] >= l.threshold
}
return false
}
// GenerateCaptcha 为指定 IP 生成验证码
func (l *LoginLimiter) GenerateCaptcha(ip string) Captcha {
l.mu.Lock()
defer l.mu.Unlock()
capd := base64Captcha.NewDriverString(50, 150, 5, 10, 4, "1234567890abcdefghijklmnopqrstuvwxyz", nil, nil, nil)
b64cap := base64Captcha.NewCaptcha(capd, base64Captcha.DefaultMemStore)
id, b64s, answer, err := b64cap.Generate()
if err != nil {
global.Logger.Error("Generate captcha failed: " + err.Error())
return Captcha{}
}
// 保存验证码到对应 IP
l.captchas[ip] = Captcha{
Id: id,
B64: b64s,
Code: answer,
ExpiresAt: time.Now().Add(5 * time.Minute),
}
return l.captchas[ip]
}
// VerifyCaptcha 验证指定 IP 的验证码
func (l *LoginLimiter) VerifyCaptcha(ip, code string) bool {
l.mu.RLock()
defer l.mu.RUnlock()
// 检查验证码是否存在且未过期
if captcha, exists := l.captchas[ip]; exists && time.Now().Before(captcha.ExpiresAt) {
return captcha.Code == code
}
return false
}
// RemoveCaptcha 移除指定 IP 的验证码
func (l *LoginLimiter) RemoveCaptcha(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
delete(l.captchas, ip)
}
// CleanupExpired 清理过期的记录
func (l *LoginLimiter) CleanupExpired() {
l.mu.Lock()
defer l.mu.Unlock()
now := time.Now()
for ip, lastTime := range l.timestamp {
if now.Sub(lastTime) > l.expiry {
delete(l.failCount, ip)
delete(l.timestamp, ip)
delete(l.captchas, ip)
}
}
}
func (l *LoginLimiter) RemoveRecord(ip string) {
l.mu.Lock()
defer l.mu.Unlock()
delete(l.failCount, ip)
delete(l.timestamp, ip)
delete(l.captchas, ip)
}
var loginLimiter = NewLoginLimiter(3, 5*time.Minute)
// Login 登录
// @Tags 登录
// @Summary 登录
@@ -29,20 +152,10 @@ type Login struct {
// @Router /admin/login [post]
// @Security token
func (ct *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Fail(c, 101, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
// 检查登录限制
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
_, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
f := &admin.Login{}
err := c.ShouldBindJSON(f)
clientIp := c.ClientIP()
if err != nil {
loginLimiter.RecordFailedAttempt(clientIp)
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
@@ -50,15 +163,14 @@ func (ct *Login) Login(c *gin.Context) {
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
loginLimiter.RecordFailedAttempt(clientIp)
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), clientIp))
response.Fail(c, 101, errList[0])
return
}
// 检查是否需要验证码
if needCaptcha {
if f.CaptchaId == "" || f.Captcha == "" || !loginLimiter.VerifyCaptcha(f.CaptchaId, f.Captcha) {
if loginLimiter.NeedsCaptcha(clientIp) {
if f.Captcha == "" || !loginLimiter.VerifyCaptcha(clientIp, f.Captcha) {
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError"))
return
}
@@ -68,19 +180,17 @@ func (ct *Login) Login(c *gin.Context) {
if u.Id == 0 {
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), clientIp))
loginLimiter.RecordFailedAttempt(clientIp)
if _, needCaptcha = loginLimiter.CheckSecurityStatus(clientIp); needCaptcha {
response.Fail(c, 110, response.TranslateMsg(c, "UsernameOrPasswordError"))
} else {
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
loginLimiter.RecordFailure(clientIp)
if loginLimiter.NeedsCaptcha(clientIp) {
loginLimiter.RemoveCaptcha(clientIp)
}
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
}
if !service.AllService.UserService.CheckUserEnable(u) {
if needCaptcha {
response.Fail(c, 110, response.TranslateMsg(c, "UserDisabled"))
return
if loginLimiter.NeedsCaptcha(clientIp) {
loginLimiter.RemoveCaptcha(clientIp)
}
response.Fail(c, 101, response.TranslateMsg(c, "UserDisabled"))
return
@@ -95,37 +205,23 @@ func (ct *Login) Login(c *gin.Context) {
Platform: f.Platform,
})
// 登录成功清除登录限制
loginLimiter.RemoveAttempts(clientIp)
// 成功清除记录
loginLimiter.RemoveRecord(clientIp)
// 清理过期记录
go loginLimiter.CleanupExpired()
responseLoginSuccess(c, u, ut.Token)
}
func (ct *Login) Captcha(c *gin.Context) {
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
if banned {
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
return
}
if !needCaptcha {
if !loginLimiter.NeedsCaptcha(clientIp) {
response.Fail(c, 101, response.TranslateMsg(c, "NoCaptchaRequired"))
return
}
err, captcha := loginLimiter.RequireCaptcha()
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError")+err.Error())
return
}
err, b64 := loginLimiter.DrawCaptcha(captcha.Content)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError")+err.Error())
return
}
captcha := loginLimiter.GenerateCaptcha(clientIp)
response.Success(c, gin.H{
"captcha": gin.H{
"id": captcha.Id,
"b64": b64,
},
"captcha": captcha,
})
}
@@ -157,20 +253,12 @@ func (ct *Login) Logout(c *gin.Context) {
// @Failure 500 {object} response.ErrorResponse
// @Router /admin/login-options [post]
func (ct *Login) LoginOptions(c *gin.Context) {
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
if banned {
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
return
}
ip := c.ClientIP()
ops := service.AllService.OauthService.GetOauthProviders()
response.Success(c, gin.H{
"ops": ops,
"register": global.Config.App.Register,
"need_captcha": needCaptcha,
"disable_pwd": global.Config.App.DisablePwdLogin,
"auto_oidc": global.Config.App.DisablePwdLogin && len(ops) == 1,
"need_captcha": loginLimiter.NeedsCaptcha(ip),
})
}
@@ -191,13 +279,13 @@ func (ct *Login) OidcAuth(c *gin.Context) {
return
}
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error()))
return
}
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
Action: service.OauthActionTypeLogin,
Op: f.Op,
Id: f.Id,
@@ -205,12 +293,10 @@ func (ct *Login) OidcAuth(c *gin.Context) {
// DeviceOs: ct.Platform(c),
DeviceOs: f.DeviceInfo.Os,
Uuid: f.Uuid,
Verifier: verifier,
Nonce: nonce,
}, 5*60)
response.Success(c, gin.H{
"code": state,
"code": code,
"url": url,
})
}

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)
@@ -98,10 +98,10 @@ func (abc *AddressBookCollection) Update(c *gin.Context) {
return
}
u := service.AllService.UserService.CurUser(c)
//if f.UserId != u.Id {
// response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
// return
//}
if f.UserId != u.Id {
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
ex := service.AllService.AddressBookService.CollectionInfoById(f.Id)
if ex.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)
@@ -100,21 +100,21 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
//check to_id
if t.Type == model.ShareAddressBookRuleTypePersonal {
if t.ToId == t.UserId {
return "CannotShareToSelf", false
return "ParamsError", false
}
tou := service.AllService.UserService.InfoById(t.ToId)
if tou.Id == 0 {
return "ItemNotFound", false
}
//非管理员不能分享给非本组织用户
//if tou.GroupId != u.GroupId {
// return "NoAccess", false
//}
if tou.GroupId != u.GroupId {
return "NoAccess", false
}
} else if t.Type == model.ShareAddressBookRuleTypeGroup {
//非管理员不能分享给其他组
//if t.ToId != u.GroupId {
// return "NoAccess", false
//}
if t.ToId != u.GroupId {
return "NoAccess", false
}
tog := service.AllService.GroupService.InfoById(t.ToId)
if tog.Id == 0 {
@@ -124,7 +124,7 @@ func (abcr *AddressBookCollectionRule) CheckForm(u *model.User, t *model.Address
return "ParamsError", false
}
// 重复检查
ex := service.AllService.AddressBookService.RuleInfoByToIdAndCid(t.Type, t.ToId, t.CollectionId)
ex := service.AllService.AddressBookService.RulePersonalInfoByToIdAndCid(t.ToId, t.CollectionId)
if t.Id == 0 && ex.Id > 0 {
return "ItemExists", false
}

View File

@@ -1,12 +1,12 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,10 +1,10 @@
package my
import (
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"time"
)

View File

@@ -1,11 +1,11 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,11 +1,11 @@
package my
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,14 +1,13 @@
package admin
import (
"strconv"
"Gwen/global"
"Gwen/http/request/admin"
adminReq "Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
adminReq "github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"strconv"
)
type Oauth struct {
@@ -44,22 +43,20 @@ func (o *Oauth) ToBind(c *gin.Context) {
return
}
err, state, verifier, nonce, url := service.AllService.OauthService.BeginAuth(f.Op)
err, code, url := service.AllService.OauthService.BeginAuth(f.Op)
if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error()))
return
}
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
Action: service.OauthActionTypeBind,
Op: f.Op,
UserId: u.Id,
Verifier: verifier,
Nonce: nonce,
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
Action: service.OauthActionTypeBind,
Op: f.Op,
UserId: u.Id,
}, 5*60)
response.Success(c, gin.H{
"code": state,
"code": code,
"url": url,
})
}
@@ -69,16 +66,16 @@ func (o *Oauth) Confirm(c *gin.Context) {
j := &adminReq.OauthConfirmForm{}
err := c.ShouldBindJSON(j)
if err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
response.Fail(c, 101, "参数错误"+err.Error())
return
}
if j.Code == "" {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
response.Fail(c, 101, "参数错误: code 不存在")
return
}
v := service.AllService.OauthService.GetOauthCache(j.Code)
if v == nil {
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
response.Fail(c, 101, "授权已过期")
return
}
u := service.AllService.UserService.CurUser(c)

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
"time"
@@ -108,15 +108,6 @@ func (ct *Peer) List(c *gin.Context) {
if query.Uuids != "" {
tx.Where("uuid in (?)", query.Uuids)
}
if query.Username != "" {
tx.Where("username like ?", "%"+query.Username+"%")
}
if query.Ip != "" {
tx.Where("last_online_ip like ?", "%"+query.Ip+"%")
}
if query.Alias != "" {
tx.Where("alias like ?", "%"+query.Alias+"%")
}
})
response.Success(c, res)
}

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
)
type Rustdesk struct {
@@ -119,16 +119,7 @@ func (r *Rustdesk) SendCmd(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
port := 0
switch rc.Target {
case model.ServerCmdTargetIdServer:
port = global.Config.Admin.IdServerPort - 1
case model.ServerCmdTargetRelayServer:
port = global.Config.Admin.RelayServerPort
}
res, err := service.AllService.ServerCmdService.SendCmd(port, rc.Cmd, rc.Option)
res, err := service.AllService.ServerCmdService.SendCmd(rc.Target, rc.Cmd, rc.Option)
if err != nil {
response.Fail(c, 101, err.Error())
return

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,11 +1,11 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
"strconv"
)

View File

@@ -1,14 +1,13 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
adResp "Gwen/http/response/admin"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
adResp "github.com/lejianwen/rustdesk-api/v2/http/response/admin"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"gorm.io/gorm"
"strconv"
)
@@ -244,10 +243,11 @@ func (ct *User) ChangeCurPwd(c *gin.Context) {
return
}
u := service.AllService.UserService.CurUser(c)
// Verify the old password only when the account already has one set
// If the password is not empty, the old password is verified
// otherwise, the old password is not verified
if !service.AllService.UserService.IsPasswordEmptyByUser(u) {
ok, _, err := utils.VerifyPassword(u.Password, f.OldPassword)
if err != nil || !ok {
oldPwd := service.AllService.UserService.EncryptPassword(f.OldPassword)
if u.Password != oldPwd {
response.Fail(c, 101, response.TranslateMsg(c, "OldPasswordError"))
return
}
@@ -296,12 +296,32 @@ func (ct *User) MyOauth(c *gin.Context) {
// groupUsers
func (ct *User) GroupUsers(c *gin.Context) {
aG := service.AllService.GroupService.List(1, 999, nil)
aU := service.AllService.UserService.List(1, 9999, nil)
response.Success(c, gin.H{
"groups": aG.Groups,
"users": aU.Users,
q := &admin.GroupUsersQuery{}
if err := c.ShouldBindJSON(q); err != nil {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
u := service.AllService.UserService.CurUser(c)
gid := u.GroupId
uid := u.Id
if service.AllService.UserService.IsAdmin(u) && q.UserId > 0 {
nu := service.AllService.UserService.InfoById(q.UserId)
gid = nu.GroupId
uid = q.UserId
}
res := service.AllService.UserService.List(1, 999, func(tx *gorm.DB) {
tx.Where("group_id = ?", gid)
})
var data []*adResp.GroupUsersPayload
for _, _u := range res.Users {
gup := &adResp.GroupUsersPayload{}
gup.FromUser(_u)
if _u.Id == uid {
gup.Status = 0
}
data = append(data, gup)
}
response.Success(c, data)
}
// Register
@@ -320,22 +340,11 @@ func (ct *User) Register(c *gin.Context) {
response.Fail(c, 101, errList[0])
return
}
regStatus := model.StatusCode(global.Config.App.RegisterStatus)
// 注册状态可能未配置,默认启用
if regStatus != model.COMMON_STATUS_DISABLED && regStatus != model.COMMON_STATUS_ENABLE {
regStatus = model.COMMON_STATUS_ENABLE
}
u := service.AllService.UserService.Register(f.Username, f.Email, f.Password, regStatus)
u := service.AllService.UserService.Register(f.Username, f.Email, f.Password)
if u == nil || u.Id == 0 {
response.Fail(c, 101, response.TranslateMsg(c, "OperationFailed"))
return
}
if regStatus == model.COMMON_STATUS_DISABLED {
// 需要管理员审核
response.Fail(c, 101, response.TranslateMsg(c, "RegisterSuccessWaitAdminConfirm"))
return
}
// 注册成功后自动登录
ut := service.AllService.UserService.Login(u, &model.LoginLog{
UserId: u.Id,

View File

@@ -1,12 +1,12 @@
package admin
import (
"Gwen/global"
"Gwen/http/request/admin"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/admin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"gorm.io/gorm"
)

View File

@@ -1,16 +1,16 @@
package api
import (
"Gwen/global"
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"Gwen/utils"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"net/http"
"strconv"
"strings"

View File

@@ -1,12 +1,12 @@
package api
import (
request "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
request "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"time"
)

View File

@@ -1,12 +1,12 @@
package api
import (
apiReq "Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
apiReq "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)
@@ -45,7 +45,7 @@ func (g *Group) Users(c *gin.Context) {
userList = service.AllService.UserService.ListByGroupId(u.GroupId, q.Page, q.PageSize)
}
data := make([]*apiResp.UserPayload, 0, len(userList.Users))
var data []*apiResp.UserPayload
for _, user := range userList.Users {
up := &apiResp.UserPayload{}
up.FromUser(user)
@@ -88,30 +88,21 @@ func (g *Group) Peers(c *gin.Context) {
users = service.AllService.UserService.ListIdAndNameByGroupId(u.GroupId)
}
namesById := make(map[uint]string, len(users))
userIds := make([]uint, 0, len(users))
namesById := make(map[uint]string)
userIds := make([]uint, 0)
for _, user := range users {
namesById[user.Id] = user.Username
userIds = append(userIds, user.Id)
}
dGroupNameById := make(map[uint]string)
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
for _, group := range allGroup.DeviceGroups {
dGroupNameById[group.Id] = group.Name
}
peerList := service.AllService.PeerService.ListByUserIds(userIds, q.Page, q.PageSize)
data := make([]*apiResp.GroupPeerPayload, 0, len(peerList.Peers))
var data []*apiResp.GroupPeerPayload
for _, peer := range peerList.Peers {
uname, ok := namesById[peer.UserId]
if !ok {
uname = ""
}
dGroupName, ok2 := dGroupNameById[peer.GroupId]
if !ok2 {
dGroupName = ""
}
pp := &apiResp.GroupPeerPayload{}
pp.FromPeer(peer, uname, dGroupName)
pp.FromPeer(peer, uname)
data = append(data, pp)
}
@@ -120,31 +111,3 @@ func (g *Group) Peers(c *gin.Context) {
Data: data,
})
}
// Device
// @Tags 群组
// @Summary 设备
// @Description 机器
// @Accept json
// @Produce json
// @Param page query int false "页码"
// @Param pageSize query int false "每页数量"
// @Param status query int false "状态"
// @Param accessible query string false "accessible"
// @Success 200 {object} response.DataResponse
// @Failure 500 {object} response.Response
// @Router /device-group/accessible [get]
// @Security BearerAuth
func (g *Group) Device(c *gin.Context) {
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) {
response.Error(c, "Permission denied")
return
}
allGroup := service.AllService.GroupService.DeviceGroupList(1, 999, nil)
c.JSON(http.StatusOK, response.DataResponse{
Total: 0,
Data: allGroup.DeviceGroups,
})
}

View File

@@ -1,12 +1,13 @@
package api
import (
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
"os"
"time"
)
@@ -49,13 +50,13 @@ func (i *Index) Heartbeat(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{})
return
}
peer := service.AllService.PeerService.FindById(info.Id)
peer := service.AllService.PeerService.FindByUuid(info.Uuid)
if peer == nil || peer.RowId == 0 {
c.JSON(http.StatusOK, gin.H{})
return
}
//如果在40s以内则不更新
if time.Now().Unix()-peer.LastOnlineTime >= 30 {
if time.Now().Unix()-peer.LastOnlineTime > 40 {
upp := &model.Peer{RowId: peer.RowId, LastOnlineTime: time.Now().Unix(), LastOnlineIp: c.ClientIP()}
service.AllService.PeerService.Update(upp)
}
@@ -73,9 +74,13 @@ func (i *Index) Heartbeat(c *gin.Context) {
// @Router /version [get]
func (i *Index) Version(c *gin.Context) {
//读取resources/version文件
v := service.AllService.AppService.GetAppVersion()
v, err := os.ReadFile("resources/version")
if err != nil {
response.Fail(c, 101, err.Error())
return
}
response.Success(
c,
v,
string(v),
)
}

View File

@@ -1,15 +1,15 @@
package api
import (
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)
@@ -27,20 +27,10 @@ type Login struct {
// @Failure 500 {object} response.ErrorResponse
// @Router /login [post]
func (l *Login) Login(c *gin.Context) {
if global.Config.App.DisablePwdLogin {
response.Error(c, response.TranslateMsg(c, "PwdLoginDisabled"))
return
}
// 检查登录限制
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
f := &api.LoginForm{}
err := c.ShouldBindJSON(f)
//fmt.Println(f)
if err != nil {
loginLimiter.RecordFailedAttempt(clientIp)
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Error(c, response.TranslateMsg(c, "ParamsError")+err.Error())
return
@@ -48,7 +38,6 @@ func (l *Login) Login(c *gin.Context) {
errList := global.Validator.ValidStruct(c, f)
if len(errList) > 0 {
loginLimiter.RecordFailedAttempt(clientIp)
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "ParamsError", c.RemoteIP(), c.ClientIP()))
response.Error(c, errList[0])
return
@@ -57,7 +46,6 @@ func (l *Login) Login(c *gin.Context) {
u := service.AllService.UserService.InfoByUsernamePassword(f.Username, f.Password)
if u.Id == 0 {
loginLimiter.RecordFailedAttempt(clientIp)
global.Logger.Warn(fmt.Sprintf("Login Fail: %s %s %s", "UsernameOrPasswordError", c.RemoteIP(), c.ClientIP()))
response.Error(c, response.TranslateMsg(c, "UsernameOrPasswordError"))
return

View File

@@ -1,17 +1,14 @@
package api
import (
"net/http"
"Gwen/global"
"Gwen/http/request/api"
"Gwen/http/response"
apiResp "Gwen/http/response/api"
"Gwen/model"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/model"
"github.com/lejianwen/rustdesk-api/v2/service"
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"net/http"
)
type Oauth struct {
@@ -35,14 +32,15 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
}
oauthService := service.AllService.OauthService
err, state, verifier, nonce, url := oauthService.BeginAuth(f.Op)
var code string
var url string
err, code, url = oauthService.BeginAuth(f.Op)
if err != nil {
response.Error(c, response.TranslateMsg(c, err.Error()))
return
}
service.AllService.OauthService.SetOauthCache(state, &service.OauthCacheItem{
service.AllService.OauthService.SetOauthCache(code, &service.OauthCacheItem{
Action: service.OauthActionTypeLogin,
Id: f.Id,
Op: f.Op,
@@ -50,12 +48,10 @@ func (o *Oauth) OidcAuth(c *gin.Context) {
DeviceName: f.DeviceInfo.Name,
DeviceOs: f.DeviceInfo.Os,
DeviceType: f.DeviceInfo.Type,
Verifier: verifier,
Nonce: nonce,
}, 5*60)
//fmt.Println("code url", code, url)
c.JSON(http.StatusOK, gin.H{
"code": state,
"code": code,
"url": url,
})
}
@@ -80,8 +76,7 @@ func (o *Oauth) OidcAuthQueryPre(c *gin.Context) (*model.User, *model.UserToken)
// 如果 UserId 为 0说明还在授权中
if v.UserId == 0 {
//fix: 1.4.2 webclient oidc
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind", "error": "No authed oidc is found"})
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind"})
return nil, nil
}
@@ -144,14 +139,11 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
// @Produce json
// @Success 200 {object} apiResp.LoginRes
// @Failure 500 {object} response.ErrorResponse
// @Router /oidc/callback [get]
// @Router /oauth/callback [get]
func (o *Oauth) OauthCallback(c *gin.Context) {
state := c.Query("state")
if state == "" {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "ParamIsEmpty",
"sub_message": "state",
})
c.String(http.StatusInternalServerError, response.TranslateParamMsg(c, "ParamIsEmpty", "state"))
return
}
cacheKey := state
@@ -159,24 +151,17 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
//从缓存中获取
oauthCache := oauthService.GetOauthCache(cacheKey)
if oauthCache == nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "OauthExpired",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthExpired"))
return
}
nonce := oauthCache.Nonce
op := oauthCache.Op
action := oauthCache.Action
verifier := oauthCache.Verifier
var user *model.User
// 获取用户信息
code := c.Query("code")
err, oauthUser := oauthService.Callback(code, verifier, op, nonce)
err, oauthUser := oauthService.Callback(code, op)
if err != nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "OauthFailed",
"sub_message": err.Error(),
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthFailed")+response.TranslateMsg(c, err.Error()))
return
}
userId := oauthCache.UserId
@@ -187,38 +172,28 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
// 检查此openid是否已经绑定过
utr := oauthService.UserThirdInfo(op, openid)
if utr.UserId > 0 {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "OauthHasBindOtherUser",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBindOtherUser"))
return
}
//绑定
user = service.AllService.UserService.InfoById(userId)
if user == nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "ItemNotFound",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ItemNotFound"))
return
}
//绑定
err := oauthService.BindOauthUser(userId, oauthUser, op)
if err != nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "BindFail",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "BindFail"))
return
}
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
"message": "BindSuccess",
})
c.String(http.StatusOK, response.TranslateMsg(c, "BindSuccess"))
return
} else if action == service.OauthActionTypeLogin {
//登录
if userId != 0 {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "OauthHasBeenSuccess",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "OauthHasBeenSuccess"))
return
}
user = service.AllService.UserService.InfoByOauthId(op, openid)
@@ -227,16 +202,15 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
if !*oauthConfig.AutoRegister {
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
oauthCache.UpdateFromOauthUser(oauthUser)
c.Redirect(http.StatusFound, "/_admin/#/oauth/bind/"+cacheKey)
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
c.Redirect(http.StatusFound, url)
return
}
//自动注册
err, user = service.AllService.UserService.RegisterByOauth(oauthUser, op)
if err != nil {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": err.Error(),
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, err.Error()))
return
}
}
@@ -252,54 +226,15 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
Type: model.LoginLogTypeOauth,
Platform: oauthService.DeviceOs,
})*/
c.Redirect(http.StatusFound, "/_admin/#/")
url := global.Config.Rustdesk.ApiServer + "/_admin/#/"
c.Redirect(http.StatusFound, url)
return
}
c.HTML(http.StatusOK, "oauth_success.html", gin.H{
"message": "OauthSuccess",
})
c.String(http.StatusOK, response.TranslateMsg(c, "OauthSuccess"))
return
} else {
c.HTML(http.StatusOK, "oauth_fail.html", gin.H{
"message": "ParamsError",
})
c.String(http.StatusInternalServerError, response.TranslateMsg(c, "ParamsError"))
return
}
}
type MessageParams struct {
Lang string `json:"lang" form:"lang"`
Title string `json:"title" form:"title"`
Msg string `json:"msg" form:"msg"`
}
func (o *Oauth) Message(c *gin.Context) {
mp := &MessageParams{}
if err := c.ShouldBindQuery(mp); err != nil {
return
}
localizer := global.Localizer(mp.Lang)
res := ""
if mp.Title != "" {
title, err := localizer.LocalizeMessage(&i18n.Message{
ID: mp.Title,
})
if err == nil {
res = utils.StringConcat(";title='", title, "';")
}
}
if mp.Msg != "" {
msg, err := localizer.LocalizeMessage(&i18n.Message{
ID: mp.Msg,
})
if err == nil {
res = utils.StringConcat(res, "msg = '", msg, "';")
}
}
//返回js内容
c.Header("Content-Type", "application/javascript")
c.String(http.StatusOK, res)
}

View File

@@ -1,12 +1,11 @@
package api
import (
"fmt"
requstform "Gwen/http/request/api"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)
@@ -14,7 +13,7 @@ type Peer struct {
}
// SysInfo
// @Tags System
// @Tags 地址
// @Summary 提交系统信息
// @Description 提交系统信息
// @Accept json
@@ -34,7 +33,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
pe := service.AllService.PeerService.FindById(f.Id)
if pe.RowId == 0 {
pe = f.ToPeer()
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
err = service.AllService.PeerService.Create(pe)
if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -42,7 +41,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
}
} else {
if pe.UserId == 0 {
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
}
fpe.RowId = pe.RowId
fpe.UserId = pe.UserId
@@ -57,20 +56,3 @@ func (p *Peer) SysInfo(c *gin.Context) {
//直接响应文本
c.String(http.StatusOK, "SYSINFO_UPDATED")
}
// SysInfoVer
// @Tags System
// @Summary 获取系统版本信息
// @Description 获取系统版本信息
// @Accept json
// @Produce json
// @Success 200 {string} string ""
// @Failure 500 {object} response.ErrorResponse
// @Router /sysinfo_ver [post]
func (p *Peer) SysInfoVer(c *gin.Context) {
//读取resources/version文件
v := service.AllService.AppService.GetAppVersion()
// 加上启动时间方便client上传信息
v = fmt.Sprintf("%s\n%s", v, service.AllService.AppService.GetStartTime())
c.String(http.StatusOK, v)
}

View File

@@ -1,9 +1,9 @@
package api
import (
apiResp "Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin"
apiResp "github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"net/http"
)

View File

@@ -1,11 +1,11 @@
package api
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/http/response/api"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/http/response/api"
"github.com/lejianwen/rustdesk-api/v2/service"
"time"
)

View File

@@ -1,9 +1,9 @@
package web
import (
"fmt"
"Gwen/global"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"strconv"
)
type Index struct {
@@ -15,21 +15,13 @@ func (i *Index) Index(c *gin.Context) {
func (i *Index) ConfigJs(c *gin.Context) {
apiServer := global.Config.Rustdesk.ApiServer
magicQueryonline := global.Config.Rustdesk.WebclientMagicQueryonline
tmp := fmt.Sprintf(`localStorage.setItem('api-server', '%v');
const ws2_prefix = 'wc-';
localStorage.setItem(ws2_prefix+'api-server', '%v');
magicQueryonline := strconv.Itoa(global.Config.Rustdesk.WebclientMagicQueryonline)
tmp := `
localStorage.setItem('api-server', "` + apiServer + `")
const ws2_prefix = 'wc-'
localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
window.webclient_magic_queryonline = %d;
window.ws_host = '%v';
`, apiServer, apiServer, magicQueryonline, global.Config.Rustdesk.WsHost)
// tmp := `
//localStorage.setItem('api-server', "` + apiServer + `")
//const ws2_prefix = 'wc-'
//localStorage.setItem(ws2_prefix+'api-server', "` + apiServer + `")
//
//window.webclient_magic_queryonline = ` + magicQueryonline + ``
window.webclient_magic_queryonline = ` + magicQueryonline + ``
c.Header("Content-Type", "application/javascript")
c.String(200, tmp)
}

View File

@@ -1,10 +1,10 @@
package http
import (
"Gwen/global"
"Gwen/http/middleware"
"Gwen/http/router"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
"github.com/lejianwen/rustdesk-api/v2/http/router"
"github.com/sirupsen/logrus"
"net/http"
"strings"
@@ -33,7 +33,7 @@ func ApiInit() {
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "404 not found")
})
g.Use(middleware.Logger(), middleware.Limiter(), gin.Recovery())
g.Use(middleware.Logger(), gin.Recovery())
router.WebInit(g)
router.Init(g)
router.ApiInit(g)

View File

@@ -1,25 +1,25 @@
package middleware
import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
// BackendUserAuth 后台权限验证中间件
func BackendUserAuth() gin.HandlerFunc {
// AdminAuth 后台权限验证中间件
func AdminAuth() gin.HandlerFunc {
return func(c *gin.Context) {
//测试先关闭
token := c.GetHeader("api-token")
if token == "" {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}
user, ut := service.AllService.UserService.InfoByAccessToken(token)
if user.Id == 0 {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}

View File

@@ -1,9 +1,9 @@
package middleware
import (
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
// AdminPrivilege ...
@@ -12,7 +12,7 @@ func AdminPrivilege() gin.HandlerFunc {
u := service.AllService.UserService.CurUser(c)
if !service.AllService.UserService.IsAdmin(u) {
response.Fail(c, 403, response.TranslateMsg(c, "NoAccess"))
response.Fail(c, 403, "无权限")
c.Abort()
return
}

View File

@@ -1,10 +1,10 @@
package middleware
import (
"Gwen/global"
"Gwen/http/response"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"github.com/lejianwen/rustdesk-api/v2/service"
)
func JwtAuth() gin.HandlerFunc {
@@ -12,18 +12,18 @@ func JwtAuth() gin.HandlerFunc {
//测试先关闭
token := c.GetHeader("api-token")
if token == "" {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}
uid, err := global.Jwt.ParseToken(token)
if err != nil {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}
if uid == 0 {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}
@@ -34,12 +34,12 @@ func JwtAuth() gin.HandlerFunc {
// Username: "测试用户",
//}
if user.Id == 0 {
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
response.Fail(c, 403, "请先登录")
c.Abort()
return
}
if !service.AllService.UserService.CheckUserEnable(user) {
response.Fail(c, 101, response.TranslateMsg(c, "Banned"))
response.Fail(c, 101, "你已被禁用")
c.Abort()
return
}

View File

@@ -1,22 +0,0 @@
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/response"
"net/http"
)
func Limiter() gin.HandlerFunc {
return func(c *gin.Context) {
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
banned, _ := loginLimiter.CheckSecurityStatus(clientIp)
if banned {
response.Fail(c, http.StatusLocked, response.TranslateMsg(c, "Banned"))
c.Abort()
return
}
c.Next()
}
}

View File

@@ -1,8 +1,8 @@
package middleware
import (
"Gwen/global"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/sirupsen/logrus"
)

View File

@@ -1,9 +1,9 @@
package middleware
import (
"Gwen/global"
"Gwen/service"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/service"
)
func RustAuth() gin.HandlerFunc {

View File

@@ -1,8 +1,8 @@
package admin
import (
"Gwen/model"
"encoding/json"
"github.com/lejianwen/rustdesk-api/v2/model"
)
type AddressBookForm struct {

View File

@@ -1,6 +1,6 @@
package admin
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type GroupForm struct {
Id uint `json:"id"`
@@ -22,15 +22,3 @@ func (gf *GroupForm) ToGroup() *model.Group {
group.Type = gf.Type
return group
}
type DeviceGroupForm struct {
Id uint `json:"id"`
Name string `json:"name" validate:"required"`
}
func (gf *DeviceGroupForm) ToDeviceGroup() *model.DeviceGroup {
group := &model.DeviceGroup{}
group.Id = gf.Id
group.Name = gf.Name
return group
}

View File

@@ -1,11 +1,10 @@
package admin
type Login struct {
Username string `json:"username" validate:"required" label:"用户名"`
Password string `json:"password,omitempty" validate:"required" label:"密码"`
Platform string `json:"platform" label:"平台"`
Captcha string `json:"captcha,omitempty" label:"验证码"`
CaptchaId string `json:"captcha_id,omitempty"`
Username string `json:"username" validate:"required" label:"用户名"`
Password string `json:"password,omitempty" validate:"required" label:"密码"`
Platform string `json:"platform" label:"平台"`
Captcha string `json:"captcha,omitempty" label:"验证码"`
}
type LoginLogQuery struct {

View File

@@ -1,7 +1,7 @@
package admin
import (
"github.com/lejianwen/rustdesk-api/v2/model"
"Gwen/model"
)
type BindOauthForm struct {
@@ -15,29 +15,27 @@ type UnBindOauthForm struct {
Op string `json:"op" binding:"required"`
}
type OauthForm struct {
Id uint `json:"id"`
Op string `json:"op" validate:"omitempty"`
OauthType string `json:"oauth_type" validate:"required"`
Issuer string `json:"issuer" validate:"omitempty,url"`
Scopes string `json:"scopes" validate:"omitempty"`
ClientId string `json:"client_id" validate:"required"`
ClientSecret string `json:"client_secret" validate:"required"`
AutoRegister *bool `json:"auto_register"`
PkceEnable *bool `json:"pkce_enable"`
PkceMethod string `json:"pkce_method"`
Id uint `json:"id"`
Op string `json:"op" validate:"omitempty"`
OauthType string `json:"oauth_type" validate:"required"`
Issuer string `json:"issuer" validate:"omitempty,url"`
Scopes string `json:"scopes" validate:"omitempty"`
ClientId string `json:"client_id" validate:"required"`
ClientSecret string `json:"client_secret" validate:"required"`
RedirectUrl string `json:"redirect_url" validate:"required"`
AutoRegister *bool `json:"auto_register"`
}
func (of *OauthForm) ToOauth() *model.Oauth {
oa := &model.Oauth{
Op: of.Op,
OauthType: of.OauthType,
OauthType: of.OauthType,
ClientId: of.ClientId,
ClientSecret: of.ClientSecret,
RedirectUrl: of.RedirectUrl,
AutoRegister: of.AutoRegister,
Issuer: of.Issuer,
Scopes: of.Scopes,
PkceEnable: of.PkceEnable,
PkceMethod: of.PkceMethod,
}
oa.Id = of.Id
return oa

View File

@@ -1,6 +1,6 @@
package admin
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type PeerForm struct {
RowId uint `json:"row_id" `
@@ -12,8 +12,6 @@ type PeerForm struct {
Username string `json:"username"`
Uuid string `json:"uuid"`
Version string `json:"version"`
GroupId uint `json:"group_id"`
Alias string `json:"alias"`
}
type PeerBatchDeleteForm struct {
@@ -32,8 +30,6 @@ func (f *PeerForm) ToPeer() *model.Peer {
Username: f.Username,
Uuid: f.Uuid,
Version: f.Version,
GroupId: f.GroupId,
Alias: f.Alias,
}
}
@@ -43,9 +39,6 @@ type PeerQuery struct {
Id string `json:"id" form:"id"`
Hostname string `json:"hostname" form:"hostname"`
Uuids string `json:"uuids" form:"uuids"`
Ip string `json:"ip" form:"ip"`
Username string `json:"username" form:"username"`
Alias string `json:"alias" form:"alias"`
}
type SimpleDataQuery struct {

View File

@@ -1,6 +1,6 @@
package admin
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type TagForm struct {
Id uint `json:"id"`

View File

@@ -1,7 +1,7 @@
package admin
import (
"github.com/lejianwen/rustdesk-api/v2/model"
"Gwen/model"
)
type UserForm struct {
@@ -14,7 +14,6 @@ type UserForm struct {
GroupId uint `json:"group_id" validate:"required"`
IsAdmin *bool `json:"is_admin" `
Status model.StatusCode `json:"status" validate:"required,gte=0"`
Remark string `json:"remark"`
}
func (uf *UserForm) FromUser(user *model.User) *UserForm {
@@ -26,7 +25,6 @@ func (uf *UserForm) FromUser(user *model.User) *UserForm {
uf.GroupId = user.GroupId
uf.IsAdmin = user.IsAdmin
uf.Status = user.Status
uf.Remark = user.Remark
return uf
}
func (uf *UserForm) ToUser() *model.User {
@@ -39,7 +37,6 @@ func (uf *UserForm) ToUser() *model.User {
user.GroupId = uf.GroupId
user.IsAdmin = uf.IsAdmin
user.Status = uf.Status
user.Remark = uf.Remark
return user
}

View File

@@ -1,9 +1,9 @@
package api
import (
"Gwen/global"
"Gwen/model"
"encoding/json"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/model"
"strconv"
)

View File

@@ -1,6 +1,6 @@
package api
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type AddressBookFormData struct {
Tags []string `json:"tags"`

View File

@@ -40,14 +40,14 @@ type LoginForm struct {
type UserListQuery struct {
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
PageSize uint `json:"pageSize" form:"pageSize" validate:"required" label:"每页数量"`
PageSize uint `json:"page_size" form:"page_size" validate:"required" label:"每页数量"`
Status int `json:"status" form:"status" label:"状态"`
Accessible string `json:"accessible" form:"accessible"`
}
type PeerListQuery struct {
Page uint `json:"page" form:"page" validate:"required" label:"页码"`
PageSize uint `json:"pageSize" form:"pageSize" validate:"required" label:"每页数量"`
PageSize uint `json:"page_size" form:"page_size" validate:"required" label:"每页数量"`
Status int `json:"status" form:"status" label:"状态"`
Accessible string `json:"accessible" form:"accessible"`
}

View File

@@ -1,6 +1,6 @@
package admin
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type LoginPayload struct {
Username string `json:"username"`
@@ -22,3 +22,15 @@ type UserOauthItem struct {
Op string `json:"op"`
Status int `json:"status"`
}
type GroupUsersPayload struct {
Id uint `json:"id"`
Username string `json:"username"`
Status int `json:"status"`
}
func (g *GroupUsersPayload) FromUser(user *model.User) {
g.Id = user.Id
g.Username = user.Username
g.Status = 1
}

View File

@@ -1,6 +1,6 @@
package api
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
type AbList struct {
Peers []*model.AddressBook `json:"peers,omitempty"`

View File

@@ -1,6 +1,6 @@
package api
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
/*
GroupPeerPayload
@@ -32,13 +32,12 @@ https://github.com/rustdesk/rustdesk/blob/master/flutter/lib/common/hbbs/hbbs.da
}
*/
type GroupPeerPayload struct {
Id string `json:"id"`
Info *PeerPayloadInfo `json:"info"`
Status int `json:"status"`
User string `json:"user"`
UserName string `json:"user_name"`
Note string `json:"note"`
DeviceGroupName string `json:"device_group_name"`
Id string `json:"id"`
Info *PeerPayloadInfo `json:"info"`
Status int `json:"status"`
User string `json:"user"`
UserName string `json:"user_name"`
Note string `json:"note"`
}
type PeerPayloadInfo struct {
DeviceName string `json:"device_name"`
@@ -60,7 +59,7 @@ func (gpp *GroupPeerPayload) FromAddressBook(a *model.AddressBook, username stri
gpp.UserName = username
}
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string, dGroupName string) {
func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string) {
gpp.Id = p.Id
gpp.Info = &PeerPayloadInfo{
DeviceName: p.Hostname,
@@ -69,5 +68,4 @@ func (gpp *GroupPeerPayload) FromPeer(p *model.Peer, username string, dGroupName
}
gpp.Note = ""
gpp.UserName = username
gpp.DeviceGroupName = dGroupName
}

View File

@@ -1,6 +1,6 @@
package api
import "github.com/lejianwen/rustdesk-api/v2/model"
import "Gwen/model"
/*
pub enum UserStatus {

View File

@@ -1,7 +1,7 @@
package api
import (
"github.com/lejianwen/rustdesk-api/v2/model"
"Gwen/model"
"time"
)

View File

@@ -1,9 +1,9 @@
package response
import (
"Gwen/global"
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/nicksnyder/go-i18n/v2/i18n"
"net/http"
)

View File

@@ -1,12 +1,12 @@
package router
import (
_ "Gwen/docs/admin"
"Gwen/global"
"Gwen/http/controller/admin"
"Gwen/http/controller/admin/my"
"Gwen/http/middleware"
"github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/admin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin"
"github.com/lejianwen/rustdesk-api/v2/http/controller/admin/my"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
@@ -22,10 +22,7 @@ func Init(g *gin.Engine) {
adg := g.Group("/api/admin")
LoginBind(adg)
adg.POST("/user/register", (&admin.User{}).Register)
ConfigBind(adg)
adg.Use(middleware.BackendUserAuth())
adg.Use(middleware.AdminAuth())
//FileBind(adg)
UserBind(adg)
GroupBind(adg)
@@ -38,6 +35,7 @@ func Init(g *gin.Engine) {
AddressBookCollectionBind(adg)
AddressBookCollectionRuleBind(adg)
UserTokenBind(adg)
ConfigBind(adg)
//deprecated by ConfigBind
//rs := &admin.Rustdesk{}
@@ -49,7 +47,7 @@ func Init(g *gin.Engine) {
MyBind(adg)
RustdeskCmdBind(adg)
DeviceGroupBind(adg)
//访问静态文件
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
}
@@ -106,18 +104,6 @@ func GroupBind(rg *gin.RouterGroup) {
}
}
func DeviceGroupBind(rg *gin.RouterGroup) {
aR := rg.Group("/device_group").Use(middleware.AdminPrivilege())
{
cont := &admin.DeviceGroup{}
aR.GET("/list", cont.List)
aR.GET("/detail/:id", cont.Detail)
aR.POST("/create", cont.Create)
aR.POST("/update", cont.Update)
aR.POST("/delete", cont.Delete)
}
}
func TagBind(rg *gin.RouterGroup) {
aR := rg.Group("/tag").Use(middleware.AdminPrivilege())
{
@@ -235,13 +221,9 @@ func UserTokenBind(rg *gin.RouterGroup) {
func ConfigBind(rg *gin.RouterGroup) {
aR := rg.Group("/config")
rs := &admin.Config{}
aR.GET("/admin", rs.AdminConfig)
aR.Use(middleware.BackendUserAuth())
aR.GET("/server", rs.ServerConfig)
aR.GET("/app", rs.AppConfig)
aR.GET("/admin", rs.AdminConfig)
}
/*

View File

@@ -1,11 +1,11 @@
package router
import (
_ "Gwen/docs/api"
"Gwen/global"
"Gwen/http/controller/api"
"Gwen/http/middleware"
"github.com/gin-gonic/gin"
_ "github.com/lejianwen/rustdesk-api/v2/docs/api"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
"github.com/lejianwen/rustdesk-api/v2/http/middleware"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"net/http"
@@ -18,8 +18,6 @@ func ApiInit(g *gin.Engine) {
if global.Config.App.ShowSwagger == 1 {
g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("api")))
}
// 加载 HTML 模板
g.LoadHTMLGlob("resources/templates/*")
frg := g.Group("/api")
@@ -48,17 +46,11 @@ func ApiInit(g *gin.Engine) {
//api/oauth/callback
frg.GET("/oauth/callback", o.OauthCallback)
frg.GET("/oauth/login", o.OauthCallback)
frg.GET("/oauth/msg", o.Message)
frg.GET("/oidc/callback", o.OauthCallback)
frg.GET("/oidc/login", o.OauthCallback)
frg.GET("/oidc/msg", o.Message)
}
{
pe := &api.Peer{}
//提交系统信息
frg.POST("/sysinfo", pe.SysInfo)
frg.POST("/sysinfo_ver", pe.SysInfoVer)
}
if global.Config.App.WebClient == 1 {
@@ -87,8 +79,6 @@ func ApiInit(g *gin.Engine) {
gr := &api.Group{}
frg.GET("/users", gr.Users)
frg.GET("/peers", gr.Peers)
// /api/device-group/accessible?current=1&pageSize=100
frg.GET("/device-group/accessible", gr.Device)
}
{
@@ -98,7 +88,6 @@ func ApiInit(g *gin.Engine) {
//更新地址
frg.POST("/ab", ab.UpAb)
}
PersonalRoutes(frg)
//访问静态文件
g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/public/upload"))

View File

@@ -1,9 +1,9 @@
package router
import (
"Gwen/global"
"Gwen/http/controller/web"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/web"
"net/http"
)

View File

@@ -1,6 +1,7 @@
package orm
import (
"Gwen/global"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
@@ -9,14 +10,14 @@ import (
)
type MysqlConfig struct {
Dsn string
Dns string
MaxIdleConns int
MaxOpenConns int
}
func NewMysql(mysqlConf *MysqlConfig, logwriter logger.Writer) *gorm.DB {
func NewMysql(mysqlConf *MysqlConfig) *gorm.DB {
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: mysqlConf.Dsn, // DSN data source name
DSN: mysqlConf.Dns, // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
//DisableDatetimePrecision: true, // 禁用 datetime 精度MySQL 5.6 之前的数据库不支持
//DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
@@ -25,7 +26,7 @@ func NewMysql(mysqlConf *MysqlConfig, logwriter logger.Writer) *gorm.DB {
}), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New(
logwriter, // io writer
global.Logger, // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Warn, // Log level

View File

@@ -1,45 +0,0 @@
package orm
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"time"
)
type PostgresqlConfig struct {
Dsn string
MaxIdleConns int
MaxOpenConns int
}
func NewPostgresql(conf *PostgresqlConfig, logwriter logger.Writer) *gorm.DB {
db, err := gorm.Open(postgres.Open(conf.Dsn), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New(
logwriter, // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Warn, // Log level
//IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
ParameterizedQueries: true, // Don't include params in the SQL log
Colorful: true,
},
),
})
if err != nil {
fmt.Println(err)
}
sqlDB, err2 := db.DB()
if err2 != nil {
fmt.Println(err2)
}
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(conf.MaxIdleConns)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(conf.MaxOpenConns)
return db
}

View File

@@ -1,6 +1,7 @@
package orm
import (
"Gwen/global"
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
@@ -13,11 +14,11 @@ type SqliteConfig struct {
MaxOpenConns int
}
func NewSqlite(sqliteConf *SqliteConfig, logwriter logger.Writer) *gorm.DB {
func NewSqlite(sqliteConf *SqliteConfig) *gorm.DB {
db, err := gorm.Open(sqlite.Open("./data/rustdeskapi.db"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New(
logwriter, // io writer
global.Logger, // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Warn, // Log level

View File

@@ -1,6 +1,6 @@
package model
import "github.com/lejianwen/rustdesk-api/v2/model/custom_types"
import "Gwen/model/custom_types"
// final String id;
// String hash; // personal ab hash password

View File

@@ -16,14 +16,3 @@ type GroupList struct {
Groups []*Group `json:"list"`
Pagination
}
type DeviceGroup struct {
IdModel
Name string `json:"name" gorm:"default:'';not null;"`
TimeModel
}
type DeviceGroupList struct {
DeviceGroups []*DeviceGroup `json:"list"`
Pagination
}

View File

@@ -1,7 +1,7 @@
package model
import (
"github.com/lejianwen/rustdesk-api/v2/model/custom_types"
"Gwen/model/custom_types"
)
type StatusCode int

View File

@@ -14,15 +14,12 @@ const (
OauthTypeGoogle string = "google"
OauthTypeOidc string = "oidc"
OauthTypeWebauth string = "webauth"
OauthTypeLinuxdo string = "linuxdo"
PKCEMethodS256 string = "S256"
PKCEMethodPlain string = "plain"
)
// Validate the oauth type
func ValidateOauthType(oauthType string) error {
switch oauthType {
case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth, OauthTypeLinuxdo:
case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth:
return nil
default:
return errors.New("invalid Oauth type")
@@ -30,9 +27,8 @@ func ValidateOauthType(oauthType string) error {
}
const (
UserEndpointGithub string = "https://api.github.com/user"
UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
IssuerGoogle string = "https://accounts.google.com"
UserEndpointGithub string = "https://api.github.com/user"
IssuerGoogle string = "https://accounts.google.com"
)
type Oauth struct {
@@ -41,12 +37,10 @@ type Oauth struct {
OauthType string `json:"oauth_type"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
//RedirectUrl string `json:"redirect_url"`
RedirectUrl string `json:"redirect_url"`
AutoRegister *bool `json:"auto_register"`
Scopes string `json:"scopes"`
Issuer string `json:"issuer"`
PkceEnable *bool `json:"pkce_enable"`
PkceMethod string `json:"pkce_method"`
TimeModel
}
@@ -62,8 +56,6 @@ func (oa *Oauth) FormatOauthInfo() error {
oa.Op = OauthTypeGithub
case OauthTypeGoogle:
oa.Op = OauthTypeGoogle
case OauthTypeLinuxdo:
oa.Op = OauthTypeLinuxdo
}
// check if the op is empty, set the default value
op := strings.TrimSpace(oa.Op)
@@ -76,13 +68,6 @@ func (oa *Oauth) FormatOauthInfo() error {
if oauthType == OauthTypeGoogle && issuer == "" {
oa.Issuer = IssuerGoogle
}
if oa.PkceEnable == nil {
oa.PkceEnable = new(bool)
*oa.PkceEnable = false
}
if oa.PkceMethod == "" {
oa.PkceMethod = PKCEMethodS256
}
return nil
}
@@ -156,24 +141,6 @@ func (gu *GithubUser) ToOauthUser() *OauthUser {
}
}
type LinuxdoUser struct {
OauthUserBase
Id int `json:"id"`
Username string `json:"username"`
Avatar string `json:"avatar_url"`
}
func (lu *LinuxdoUser) ToOauthUser() *OauthUser {
return &OauthUser{
OpenId: strconv.Itoa(lu.Id),
Name: lu.Name,
Username: strings.ToLower(lu.Username),
Email: lu.Email,
VerifiedEmail: true, // linux.do 用户邮箱默认已验证
Picture: lu.Avatar,
}
}
type OauthList struct {
Oauths []*Oauth `json:"list"`
Pagination

View File

@@ -14,8 +14,6 @@ type Peer struct {
User *User `json:"user,omitempty"`
LastOnlineTime int64 `json:"last_online_time" gorm:"default:0;not null;"`
LastOnlineIp string `json:"last_online_ip" gorm:"default:'';not null;"`
GroupId uint `json:"group_id" gorm:"default:0;not null;index"`
Alias string `json:"alias" gorm:"default:'';not null;index"`
TimeModel
}

View File

@@ -11,7 +11,6 @@ type User struct {
GroupId uint `json:"group_id" gorm:"default:0;not null;index"`
IsAdmin *bool `json:"is_admin" gorm:"default:0;not null;"`
Status StatusCode `json:"status" gorm:"default:1;not null;"`
Remark string `json:"remark" gorm:"default:'';not null;"`
TimeModel
}

View File

@@ -2,11 +2,11 @@ package model
type UserToken struct {
IdModel
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
DeviceUuid string `json:"device_uuid" gorm:"default:'';omitempty;"`
DeviceId string `json:"device_id" gorm:"default:'';omitempty;"`
Token string `json:"token" gorm:"default:'';not null;index"`
ExpiredAt int64 `json:"expired_at" gorm:"default:0;not null;"`
UserId uint `json:"user_id" gorm:"default:0;not null;index"`
DeviceUuid string `json:"device_uuid" gorm:"default:'';omitempty;"`
DeviceId string `json:"device_id" gorm:"default:'';omitempty;"`
Token string `json:"token" gorm:"default:'';not null;index"`
ExpiredAt int64 `json:"expired_at" gorm:"default:0;not null;"`
TimeModel
}

View File

@@ -33,11 +33,6 @@ description = "No access."
one = "No access."
other = "No access."
[NeedLogin]
description = "Need login."
one = "Please log in first."
other = "Please log in first."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Username or password error."
@@ -138,23 +133,3 @@ other = "Captcha required."
description = "Captcha error."
one = "Captcha error."
other = "Captcha error."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Password login disabled."
other = "Password login disabled."
[CannotShareToSelf]
description = "Cannot share to self."
one = "Cannot share to self."
other = "Cannot share to self."
[Banned]
description = "Banned."
one = "Banned."
other = "Banned."
[RegisterSuccessWaitAdminConfirm]
description = "Register success, wait admin confirm."
one = "Register success, wait admin confirm."
other = "Register success, wait admin confirm."

View File

@@ -33,11 +33,6 @@ description = "No access."
one = "Sin acceso."
other = "Sin acceso."
[NeedLogin]
description = "Need login."
one = "Por favor inicie sesión primero."
other = "Por favor inicie sesión primero."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Error de usuario o contraseña."
@@ -146,24 +141,4 @@ other = "Captcha requerido."
[CaptchaError]
description = "Captcha error."
one = "Error de captcha."
other = "Error de captcha."
[PwdLoginDisabled]
description = "Password login disabled."
one = "Inicio de sesión con contraseña deshabilitado."
other = "Inicio de sesión con contraseña deshabilitado."
[CannotShareToSelf]
description = "Cannot share to self."
one = "No se puede compartir con uno mismo."
other = "No se puede compartir con uno mismo."
[Banned]
description = "Banned."
one = "Prohibido."
other = "Prohibido."
[RegisterSuccessWaitAdminConfirm]
description = "Register success, wait admin confirm."
one = "Registro exitoso, espere la confirmación del administrador."
other = "Registro exitoso, espere la confirmación del administrador."
other = "Error de captcha."

Some files were not shown because too many files have changed in this diff Show More