Compare commits

..

379 Commits

Author SHA1 Message Date
lejianwen
c5687e1506 docs: Removed webclient2 because of DMCA 2025-09-29 09:41:07 +08:00
lejianwen
222792419f feat!: Removed webclient2 because of DMCA 2025-09-28 20:08:28 +08:00
lejianwen
04789e08d5 feat!: Removed webclient2 because of DMCA 2025-09-27 11:30:37 +08:00
lejianwen
929f6ddf20 feat: WebClient 1.4.2 2025-09-12 15:11:49 +08:00
nomoneynolife
6e3b16d425 feat:ldap allow-group (#388) 2025-09-05 12:53:45 +08:00
Tom
ce0291a8f9 feat: add TLS configuration option for MySQL (#384) 2025-09-03 19:43:16 +08:00
lejianwen
faee954f6d docs: readme 2025-09-01 21:36:27 +08:00
lejianwen
f09c898f16 feat(peer): add alias field and support filtering by alias 2025-08-31 13:39:22 +08:00
lejianwen
a7c087afbb fix!: Update peer to use ID instead of UUID 2025-08-31 12:46:54 +08:00
lejianwen
07dfba9d59 fix: The callback URL is based on the configured API SERVER because the project might be behind an Nginx reverse proxy. If the Origin/Host is forgotten to configure the reverse proxy, it will be incorrect 2025-08-10 15:48:11 +08:00
lejianwen
448725b760 fix: Normal user can not change the name of their own address book (#341) 2025-08-10 11:17:51 +08:00
Tao Chen
b6be4dea21 feat: Optimize login workflow (#345)
* add "disable_pwd" and "auto_oidc" at /admin/login-options

* fix: build RedirectURL by host and scheme, not Origin
2025-07-31 10:46:11 +08:00
caicob
862a1d431e docs: README_EN.md (#340) 2025-07-31 10:42:56 +08:00
startgo
8819c85ef4 feat: Update zh_TW.toml (#322)
Corrected translation to match Taiwanese Traditional Chinese usage
2025-07-19 21:08:40 +08:00
lejianwen
e2ec387ba2 feat(captcha): The captcha generates uppercase letter images, but it can only recognize them as lowercase (#319) 2025-07-14 20:36:33 +08:00
lejianwen
1d2d1d4c98 fix: Oauth callback url is fixed to host+/api/oidc/callback (#314) 2025-07-11 09:55:48 +08:00
Plynksiy Nikita
16b941e37f docs: add note about requiring conf and resources directories (or specifying paths via -c and RUSTDESK_API_GIN_RESOURCES_PATH) when running without docker (#311) 2025-07-09 09:43:56 +08:00
lejianwen
ff2c79a316 feat(user): add remark field to User model and form (#307) 2025-07-08 12:14:46 +08:00
lejianwen
b3aee87655 feat: Update database version constant to 264 2025-07-08 12:13:31 +08:00
k3-cat
73a8461a2d feat: Improve oauth redirect (#303)
* fix: redirects after oauth can potentially misalign with server's actually hostname

* feat: remove `RedirectURL` from oauth config, as it should checked by provider rather than client

* feat: align oauth endpoint with the hostname in requests
2025-07-04 14:30:07 +08:00
k3-cat
af4813db30 feat: better autorenewal (#305) 2025-07-04 14:27:47 +08:00
Plynksiy Nikita
9d2b589faa feat(password): Password hashing with bcrypt (#290)
* feat(password): add configurable password hashing with md5 and bcrypt

* docs: add password hashing algorithm configuration (bcrypt/md5)

* feat(password): better bcrypt fallback and minor refactoring

* feat(password): handle errors in password encryption and verification

* feat(password): remove password hashing algorithm configuration
2025-06-24 17:23:36 +08:00
Plynksiy Nikita
aa04b225b9 feat: Use crypto/rand for secure random string generation (#293) 2025-06-18 20:47:24 +08:00
Plynksiy Nikita
fb34d0ac83 fix: correct typo in build arg FRONTEND_GIT_REPO (#292) 2025-06-18 20:42:13 +08:00
Plynksiy Nikita
f68ec8f3a1 feat(i18n): replace hardcoded messages with translated strings (#289) 2025-06-17 09:05:10 +08:00
lejianwen
4b16ad29bb chore: Update Go version to 1.23 in build configurations 2025-06-16 15:41:16 +08:00
lejianwen
c554ff4a31 docs: Update swagger docs 2025-06-16 12:31:48 +08:00
lejianwen
791a1850b8 feat: Add PostgreSQL support and refactor MySQL DSN handling (#284) 2025-06-16 12:26:08 +08:00
lejianwen
175fdc3bd4 feat: Add start time in /api/sysinfover 2025-06-16 12:23:48 +08:00
lejianwen
f20414b3a5 fix(admin): Use admin-hello first
(#274) (#255)
2025-06-15 15:33:12 +08:00
puyujian
bf39a29e6c feat(oauth): 支持linux.do登录 (#280)
* 支持linux.do登录

* 修正
2025-06-15 15:32:20 +08:00
lejianwen
4ba805ac5f chore: update download links for musl cross-compilers 2025-06-05 12:14:17 +08:00
lejianwen
b2d71d4312 fix: Init sqlite fail(#266) 2025-06-04 09:31:43 +08:00
lejianwen
a35fa6f9a8 fix(server): Port custom (#257) 2025-05-30 12:27:37 +08:00
lejianwen
83baa140f5 fix: Captcha some problem when users login with same ip 2025-05-27 17:36:20 +08:00
lejianwen
b6eaa4b061 docs: Readme 2025-05-25 17:44:29 +08:00
lejianwen
d4fdf3785b feat(register): Register status can be set (#223) 2025-05-25 17:03:13 +08:00
lejianwen
f2ea022965 feat(login): Captcha upgrade and add the function to ban IP addresses (#250) 2025-05-25 16:52:58 +08:00
Tao Chen
fe4a115c9d fix: dn should be case-insensitive (#250) 2025-05-21 09:07:08 +08:00
lejianwen
4412f0309c feat(webclient): Web client to 1.4.0 2025-05-12 20:16:08 +08:00
lejianwen
dd78911ed9 fix: PageSize (#225) 2025-05-06 19:08:18 +08:00
lejianwen
b23a9bde8e chore: Noelware/docker-manifest-action 2025-04-25 16:20:36 +08:00
lejianwen
3fe7184b4b chore: Noelware/docker-manifest-action 2025-04-25 14:34:45 +08:00
lejianwen
4637879091 style: Oauth page languages 2025-04-24 21:52:43 +08:00
lejianwen
6d1184d332 style: Remove useless configurations 2025-04-15 10:52:46 +08:00
lejianwen
fb8ef8a1fd feat: Peer share to group 2025-04-14 19:12:40 +08:00
lejianwen
ae59b6a733 feat: Add SysInfoVer endpoint and AppService for version retrieval 2025-04-07 16:38:21 +08:00
lejianwen
605e88e04c fix: Update peer based on the UUID (#180) 2025-04-02 09:50:16 +08:00
lejianwen
3ef4454c05 feat: Add Korean translations validator (#168) 2025-04-02 09:42:29 +08:00
lejianwen
35be09b37c fix: Get Uuids 2025-03-15 21:02:47 +08:00
lejianwen
891469909c style: Update peer last online time logic (#173) 2025-03-15 21:02:08 +08:00
lejianwen
6ed75bc517 feat(admin): Add filter by ip and username (#172) 2025-03-15 19:49:49 +08:00
Tao Chen
caea60c732 fix: rm varify password accidentally (#176) 2025-03-15 19:40:02 +08:00
lejianwen
0a78c206de fix: Init database err (#166) 2025-03-04 18:14:25 +08:00
lejianwen
a7f7c76e0e docs: Readme 2025-03-04 16:30:12 +08:00
lejianwen
c10d132b3c fix: templates 2025-03-04 16:14:34 +08:00
lejianwen
8b951065ce style(webclient): ws-host 2025-03-04 15:48:25 +08:00
lejianwen
08420f9db6 feat(config): add ws-host configuration (#156) 2025-03-04 15:43:25 +08:00
Tao Chen
5db3c8bf0b fix: Fix/ldap tls (#162)
* optimize and fix tls of LDAP

* fix
2025-03-02 22:59:01 +08:00
lejianwen
403d9c2233 style(service): refactor global dependencies to use local variables 2025-02-26 19:15:38 +08:00
lejianwen
0f16f61ab3 feat(oauth): Oauth nonce (#148) 2025-02-26 16:36:53 +08:00
lejianwen
0dd92311b2 feat(oauth): Oauth callback page beautification (#115) 2025-02-25 13:51:49 +08:00
lejianwen
ef4af39f94 feat(api): Add device group for 1.3.8 2025-02-23 21:35:42 +08:00
lejianwen
73f3056878 feat(api): Add /device-group/accessible for 1.3.8 2025-02-23 15:32:44 +08:00
lejianwen
f0b4b0d7c6 style(oidc): Oidc style 2025-02-21 09:49:41 +08:00
lejianwen
5d5aae6b3c fix(admin): Admin hello 2025-02-20 19:37:34 +08:00
lejianwen
4b5e316475 style: No need exec sql on version 261 2025-02-20 19:22:22 +08:00
lejianwen
8ff9a12a34 fix: Js content-type 2025-02-19 16:04:03 +08:00
Tao Chen
6f55c5b642 feat(oidc): add pkce (#150) 2025-02-19 09:31:25 +08:00
lejianwen
99e63cadcf docs: Readme 2025-02-17 10:59:38 +08:00
lejianwen
c0346ff9e1 fix(config)!: Token expire time (#145)
将配置中的过期时间单位统一为time.Duration,可以设置为`h`,`m`,`s`
2025-02-17 10:49:59 +08:00
lejianwen
d85a00f748 docs: Readme 2025-02-16 21:06:55 +08:00
lejianwen
6a6a1ed02b feat: Login by pwd can be disable
---

Closes: #141
2025-02-16 13:06:45 +08:00
lejianwen
4b3bbe809d style: middleware name 2025-02-12 22:09:52 +08:00
lejianwen
63cf231bdc fix(admin): Admin web title 2025-02-12 21:10:07 +08:00
lejianwen
6aebf061a4 style: Module name 2025-02-12 19:46:39 +08:00
lejianwen
a2672c9489 style: generate 2025-02-12 16:56:41 +08:00
lejianwen
a304da6e02 style: Remove generate 2025-02-12 16:25:04 +08:00
lejianwen
0e160f110b docs: Docs 2025-02-12 16:14:51 +08:00
lejianwen
32a9eda8ca style: Module name 2025-02-12 16:07:51 +08:00
lejianwen
8622c78da0 fix: Config watchConfig (#135)
---
Closes: #135
2025-02-10 10:13:34 +08:00
lejianwen
7d6bc2a74f fix: User disabled can not work (#133)
---
Closes: #133
2025-02-10 10:13:15 +08:00
Tao Chen
5224c31d61 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
983025538f docs: Readme 2025-02-09 21:13:01 +08:00
Tao Chen
cd5b247356 docs: add LDAP info (#130) 2025-02-09 19:36:31 +08:00
lejianwen
e2d72530b1 docs: Readme 2025-02-07 18:14:12 +08:00
lejianwen
5660d40514 style: Log time 2025-02-07 17:57:28 +08:00
lejianwen
d486444680 feat: Web sso env (#125) 2025-02-07 17:52:40 +08:00
lejianwen
2261a4292a docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
829f8799dd style: Up conf 2025-02-07 17:52:40 +08:00
lejianwen
e549416d94 docs: Up readme 2025-02-07 17:52:40 +08:00
lejianwen
ff77014acb feat: Random Initial Password for Admin (#117) 2025-02-07 17:52:39 +08:00
lejianwen
daab7112be fix(build): Fix no admin in deb (#119 #120) 2025-02-03 13:29:35 +08:00
lejianwen
12442f1ba0 fix(api): Add Default Token Expire (#113) 2025-02-03 13:18:08 +08:00
Tao Chen
0e3f204327 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
d9fcfdde6a feat(api): Add api token expire
Resolves #109
2025-01-21 18:23:28 +08:00
lejianwen
44fa22b7e5 feat(api): Add api/version
Resolves #110
2025-01-20 20:04:22 +08:00
lejianwen
59038e27f8 docs: Up Swagger docs 2025-01-20 19:35:47 +08:00
lejianwen
7d23ce3caf style: Add Start Tips 2025-01-20 13:12:45 +08:00
lejianwen
7c528facf9 fix(api): Change tag to alphabetical sorting
Fixes: #108
2025-01-20 13:07:41 +08:00
lejianwen
f8548a6589 fix(docs): Api Route doc 2025-01-19 13:10:43 +08:00
lejianwen
23c5a96fe1 feat(i18n): Add ZH_TW 2025-01-19 13:10:19 +08:00
Jia-Bin
c72865b181 Add Traditional Chinese 2025-01-18 23:04:39 +08:00
lejianwen
9a2f7010e1 docs: Up readme 2025-01-16 22:01:23 +08:00
lejianwen
a72096b56d docs: Up readme 2025-01-16 21:59:46 +08:00
lejianwen
56014d35bd feat(server): Rustdesk Id Server Port & Relay Server Port #104 2025-01-16 20:57:00 +08:00
lejianwen
a7e4ce4f72 refactor(config): Up Config Load 2025-01-16 20:40:42 +08:00
lejianwen
f2cf3e1060 docs: Up readme 2025-01-15 21:56:04 +08:00
lejianwen
a4433e6113 fix: Jwt 2025-01-15 20:26:26 +08:00
lejianwen
cca191aad3 docs: Up readme 2025-01-15 20:09:08 +08:00
lejianwen
edb095ab0b feat!: Add JWT
- `RUSTDESK_API_JWT_KEY`如果设置,将会启用JWT,token自动续期功能将失效
- 此功能是为了server端校验token的合法性
2025-01-15 19:25:28 +08:00
lejianwen
ac45a1b5ad docs: Up readme 2025-01-12 23:12:00 +08:00
lejianwen
83279a553a docs: Up readme 2025-01-12 21:35:34 +08:00
lejianwen
a5671e692d feat(server): Add Rustdesk Relay Server Commands 2025-01-04 20:49:44 +08:00
lejianwen
63f2fd2f07 style(server): fmt print to log 2025-01-02 21:49:37 +08:00
lejianwen
5a09850fc4 docs: Up readme 2025-01-02 17:03:07 +08:00
lejianwen
aac44835ed fix: Fix Dockerfile_full_s6 2024-12-31 23:33:17 +08:00
lejianwen
4eed76d674 fix(server): Fix Rustdesk Sys Command 2024-12-31 23:29:05 +08:00
lejianwen
d1fc0036cb feat(server): Add Rustdesk Command
And add build full s6 image for rustdesk command
2024-12-31 23:16:15 +08:00
lejianwen
1555865612 docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:36:57 +08:00
lejianwen
147de065fa docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:35:06 +08:00
lejianwen
8dc5f82a0e docs(readme): Up readme
Connection timeout issues move to #92
2024-12-30 13:33:11 +08:00
lejianwen
3f8cb4dac5 chore(buildTest): add start.bat to run on windows 2024-12-27 20:13:55 +08:00
lejianwen
4b9e1f02f2 fix(build): add start.bat to run on windows(#89) 2024-12-27 20:09:08 +08:00
lejianwen
18312336a4 refactor(admin): Move Admin Web Route to user model 2024-12-27 19:27:33 +08:00
lejianwen
72e3a9a841 feat(admin): Add My Login log 2024-12-27 19:25:59 +08:00
lejianwen
16427164f8 feat(admin): Support Markdown to welcome msg 2024-12-25 19:21:50 +08:00
lejianwen
5c96350e7d fix(api): Get ab list when personal is disabled (#86) 2024-12-25 19:04:42 +08:00
lejianwen
1dd920d192 fix(webclient): share fail when expire is 0
Closes: #88
2024-12-25 15:01:03 +08:00
lejianwen
c41038d2d8 fix(build): fix build_test.yml 2024-12-25 14:06:29 +08:00
lejianwen
88ec961312 fix(build): up build.yml to build deb 2024-12-25 13:20:12 +08:00
lejianwen
a64b9ced1c chore(build): up build.yml to build deb 2024-12-25 12:57:11 +08:00
Follow the wind
c778990cf9 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
ba46dbbf7e chore(changelog): up build.yml to generate changelog 2024-12-22 14:14:51 +08:00
lejianwen
3b8b8f37dd chore(changelog): up build.yml to generate changelog 2024-12-22 14:04:23 +08:00
51db63cf35 feat(i18n): Merge pull request #85 from jimmyGALLAND/trans-fr
add locale french
2024-12-22 13:57:56 +08:00
jimmyGALLAND
9ebfd6de85 add locale french 2024-12-21 22:56:48 +01:00
lejianwen
1c3afa4a70 chore(changelog): up build.yml to add changelog 2024-12-21 21:39:11 +08:00
lejianwen
5cddbf14c8 docs(webclient): up readme 2024-12-21 21:31:54 +08:00
lejianwen
88765302e8 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
034ec6ca1b optimize add ab from peer #84 2024-12-20 11:11:32 +08:00
lejianwen
a55e501b50 up docs and readme 2024-12-18 14:28:06 +08:00
lejianwen
e36d307d28 fix captcha 2024-12-18 13:51:06 +08:00
lejianwen
1e15287c83 fix captcha 2024-12-18 13:36:01 +08:00
lejianwen
c298ddea0c add show-swagger config #83 2024-12-18 12:50:09 +08:00
lejianwen
41fd387baf up docs 2024-12-18 12:44:06 +08:00
lejianwen
bf8eadfbaa add captcha #82 2024-12-18 12:43:55 +08:00
lejianwen
a3012cfa18 split my from admin 2024-12-17 21:41:56 +08:00
lejianwen
592b04746d add batch add ab from peer and up my 2024-12-13 16:27:12 +08:00
lejianwen
ef0410a8b9 add share record manage 2024-12-13 12:32:36 +08:00
lejianwen
3a430c72a2 up README.md 2024-12-10 15:28:05 +08:00
lejianwen
1eaae8f213 up README.md 2024-12-09 13:43:04 +08:00
lejianwen
8c0ac5621a add batch add ab from peer
add batch update ab tags
2024-12-06 19:45:41 +08:00
lejianwen
cd74cd19ef up api docs 2024-12-06 10:36:40 +08:00
lejianwen
1c5cd3ae62 add batch delete user token 2024-12-06 10:36:27 +08:00
lejianwen
7749ff0729 up username length to 32 #70 2024-12-06 10:22:28 +08:00
lejianwen
b77a104d9c up readme 2024-11-28 12:42:15 +08:00
fc898ba30c Create LICENSE 2024-11-26 17:29:46 +08:00
lejianwen
287bbd43fb add es lang 2024-11-26 10:43:01 +08:00
lejianwen
65571ab2f5 up docs 2024-11-22 19:53:49 +08:00
lejianwen
17b65b412f up img 2024-11-20 19:33:27 +08:00
lejianwen
b06d9c4e22 up readme 2024-11-17 18:27:15 +08:00
lejianwen
601d47570e up package lock 2024-11-16 20:56:26 +08:00
lejianwen
29d6e36d50 up ws js 2024-11-16 20:03:30 +08:00
lejianwen
abca3a9300 up 2024-11-16 19:42:13 +08:00
lejianwen
22e470fee5 add webclientv2 2024-11-16 18:33:02 +08:00
lejianwen
78869a4a57 fix gitignore 2024-11-15 21:43:23 +08:00
ljw
d0f8d79efb up readme 2024-11-12 11:54:21 +08:00
ljw
5417e383c7 up readme 2024-11-12 11:53:57 +08:00
ljw
49eaa831ed add batch delete log #57 2024-11-12 09:08:10 +08:00
ljw
ae2e78e373 up admin conf 2024-11-12 08:43:49 +08:00
ljw
7bb2ce0a43 add admin conf 2024-11-11 22:26:15 +08:00
ljw
15a518c1ac up readme 2024-11-09 20:38:11 +08:00
ljw
4915cfa9ba fix #55 2024-11-09 20:14:14 +08:00
ljw
de712ba9c5 add build_test.yml 2024-11-08 16:09:50 +08:00
ljw
c58f08af27 add build_test.yml 2024-11-08 16:05:43 +08:00
ljw
5609a1a618 add cmd 2024-11-08 15:54:49 +08:00
ljw
70a03f1aba fix #52 & add auto refresh token #53 2024-11-07 10:46:00 +08:00
7c6aaa171e Merge pull request #52 from IamTaoChen/fix/bug
fix: cannot delete user
2024-11-07 10:21:50 +08:00
Tao Chen
34a27ba073 add error information 2024-11-06 15:06:15 +08:00
Tao Chen
956700c27f fix: cannot delete user 2024-11-06 14:36:12 +08:00
ljw
197a718f03 fix migrate 2024-11-05 21:07:39 +08:00
ljw
2c1892f40c fix migrate 2024-11-05 21:07:31 +08:00
ljw
943b1f8077 fix migrate 2024-11-05 21:03:32 +08:00
ljw
e71ea2243f fix username length #48 2024-11-05 11:57:16 +08:00
ljw
daeae19194 up oauth re 2024-11-05 11:48:35 +08:00
ljw
4321a41cd7 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
3acfb36c5d modify google ro re-use oidc 2024-11-04 21:30:58 +08:00
Tao Chen
5a53f180e4 fix: delete check 2024-11-03 22:23:24 +08:00
Tao Chen
ca79a63492 fix: call us.IsAdmin(u) to check admin 2024-11-03 21:59:17 +08:00
Tao Chen
9dfe745629 fix google 2024-11-03 18:04:28 +08:00
Tao Chen
18d59d7047 re-use responseLoginSuccess 2024-11-03 17:25:27 +08:00
Tao Chen
aee25a6c99 fix: last admin shouldn't be deleted, disabled or demoted 2024-11-03 17:19:05 +08:00
Tao Chen
fea3960672 fix: Github AvatarUrl to OauthUser 2024-11-03 16:49:28 +08:00
Tao Chen
da7b70c471 add err for RegisterByOauth 2024-11-03 16:49:03 +08:00
Tao Chen
3cd90c8f74 fronted for docker-dev 2024-11-03 16:34:50 +08:00
Tao Chen
fb9173ed53 optimize /admin/login-options 2024-11-03 05:37:34 +08:00
Tao Chen
b9efc73025 chroe 2024-11-03 05:34:19 +08:00
Tao Chen
64f28c17d8 add Avatar to OauthUser 2024-11-03 05:33:59 +08:00
Tao Chen
1ca50b5e9d low case email 2024-11-03 05:25:10 +08:00
Tao Chen
9ed376715f const var for op name 2024-11-03 05:13:22 +08:00
Tao Chen
6698877761 fix: email from github 2024-11-03 05:11:31 +08:00
Tao Chen
2ceaa0091b fix: Email of Register 2024-11-03 05:07:17 +08:00
Tao Chen
6fb1fbc5b1 fix: RegisterByOauth without Email 2024-11-03 04:35:39 +08:00
ljw
4909beee32 fix #45 2024-11-02 18:49:16 +08:00
Tao Chen
d85a799d0b set user_id=0 at peers, when the user is deleted 2024-11-02 08:24:07 +08:00
Tao Chen
d38117107d When login, peer doesn't exist, it should create 2024-11-02 08:19:44 +08:00
Tao Chen
6a7ef29089 delete the token when delete a peer 2024-11-02 08:02:03 +08:00
Tao Chen
a4dd39043e add MyPeers for user 2024-11-02 07:35:26 +08:00
Tao Chen
dfcc7d54c1 add email for register 2024-11-02 05:43:55 +08:00
Tao Chen
71d1c431a9 add DeviceId to userToken 2024-11-02 05:42:47 +08:00
Tao Chen
d31d669734 logout should unbind uuid and uid of peer 2024-11-02 05:07:41 +08:00
Tao Chen
c021ebfbdf fix bug ValidateOauthProvider location 2024-11-02 04:20:00 +08:00
Tao Chen
485ae54e9e re-construct oauth 2024-11-02 04:01:28 +08:00
ljw
c5e3482538 load key from file 2024-11-01 14:56:11 +08:00
c4b45a51d5 Merge pull request #42 from IamTaoChen/docker
Docker Optimize
2024-11-01 13:10:59 +08:00
Tao Chen
51186b66dc add more 2024-11-01 01:33:08 +08:00
Tao Chen
2627f6fb06 optimize scripts 2024-11-01 01:33:05 +08:00
Tao Chen
a6a6c3aefe optimize build speed, like cache and mirror 2024-11-01 01:32:45 +08:00
Tao Chen
d21b5809c4 add ARG CONTRY=CN to improve the alpinelinux install speed 2024-11-01 00:29:33 +08:00
Tao Chen
503e908eb6 add bash to run dev-docker 2024-11-01 00:28:43 +08:00
ljw
6c94566e83 add remove user token #34 2024-10-31 22:29:12 +08:00
095640af3f Merge pull request #40 from IamTaoChen/resetEmptyPassWD
Reset empty password
2024-10-31 18:46:34 +08:00
Tao Chen
5488babfb3 Merge branch 'master' into resetEmptyPassWD 2024-10-31 16:35:32 +08:00
Tao Chen
962e07d65f ommit check old passwd if password is empty 2024-10-31 16:23:06 +08:00
Tao Chen
bd2eeae01a add IsPasswordEmpty... 2024-10-31 16:22:42 +08:00
ljw
efead3f9c7 up readme #28 2024-10-31 15:48:36 +08:00
ljw
7cb838693a up readme #28 2024-10-31 15:47:39 +08:00
ljw
e2fda47cbb add register 2024-10-31 15:14:30 +08:00
ljw
0ed40318cb up oauth 2024-10-31 14:03:48 +08:00
122b3baf6f Merge pull request #36 from IamTaoChen/oidc-for-web
OIDC for web
2024-10-31 11:10:46 +08:00
Tao Chen
06f007ebd9 rename build stage 2024-10-31 09:21:43 +08:00
Tao Chen
7c1fc4fa6d add some /admin/ to surport web OIDC 2024-10-31 09:21:30 +08:00
ljw
e1424bcea7 fix buidconfirm 2024-10-30 20:59:51 +08:00
ljw
b3edc9d112 up del user 2024-10-30 19:34:56 +08:00
68065d3372 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
f6a0c6466f modify delete user 2024-10-30 16:33:01 +08:00
Tao Chen
1070022277 update README 2024-10-30 16:31:47 +08:00
Tao Chen
f9a8a966e5 update README for OIDC 2024-10-30 16:29:49 +08:00
Tao Chen
3041fb82d9 delete user from user_thirds, too 2024-10-30 15:59:33 +08:00
ljw
0ddfbdbd23 up v 2024-10-30 15:46:12 +08:00
ljw
eada376783 add docker-compose-dev.yaml 2024-10-30 15:34:45 +08:00
bc92ffc106 Merge pull request #30 from IamTaoChen/oidc
Add General OIDC Login
2024-10-30 14:40:10 +08:00
Tao Chen
60b7ccdb3a bind oidc ThirdEmail 2024-10-29 23:09:54 +08:00
Tao Chen
9eff8ea33a fix: spelling 2024-10-29 23:00:17 +08:00
Tao Chen
17305b6a6f fix bug 2024-10-29 18:48:37 +08:00
Tao Chen
7d83226655 fix bug - oidc scopes 2024-10-29 18:46:45 +08:00
ljw
001884a4d8 fix oauth register #26 #23 2024-10-29 15:31:27 +08:00
Tao Chen
461b100b6c fix bug 2024-10-29 14:27:15 +08:00
Tao Chen
4105f14a3f try add oidc 2024-10-29 11:51:01 +08:00
Tao Chen
4baa8d392e optimize docker 2024-10-29 11:50:55 +08:00
Tao Chen
bf48e8ba19 optimize build.sh 2024-10-29 10:58:17 +08:00
ljw
991fc86fa6 add last online ip #24 2024-10-28 20:24:34 +08:00
ljw
1191ade8af up address book add version #20 2024-10-28 19:48:47 +08:00
ljw
02232fc343 up peer update 2024-10-28 19:15:13 +08:00
ljw
497dfb2402 fix bug #27 2024-10-28 16:08:33 +08:00
ljw
f21fb5d437 up 2024-10-28 14:54:00 +08:00
ljw
8ee93b530d add address book name &
add share address book
2024-10-28 14:51:07 +08:00
ljw
e833c0e0c0 up readme 2024-10-23 11:09:28 +08:00
ljw
d5cf3544f1 fix group 2024-10-23 11:09:13 +08:00
ljw
ca9a3155af up build.yml 2024-10-23 09:19:02 +08:00
ljw
b1817ca63f add armv7l build #21 2024-10-23 09:03:01 +08:00
ljw
476f251d2c add ru lang 2024-10-22 19:46:38 +08:00
ljw
e2fa928648 add ko lang,but validator dont have translations ko 2024-10-22 12:21:32 +08:00
ljw
3f9b370dd6 Merge branch 'master' of https://github.com/lejianwen/rustdesk-api 2024-10-22 11:31:01 +08:00
7cf4fb4157 Merge pull request #19 from jkh0kr/master
Add Korean language file
2024-10-22 11:30:49 +08:00
ljw
6fb1235dd8 up 2024-10-22 11:28:27 +08:00
진기환
8aa779f611 Add Korean language file 2024-10-22 10:35:58 +09:00
ljw
5c4b6b5cd0 up readme 2024-10-21 21:35:27 +08:00
ljw
47ab3958c9 add file conn log 2024-10-21 21:08:25 +08:00
ljw
cbb3764b60 up build.yml 2024-10-20 20:26:08 +08:00
ljw
f5aadec6c0 fix group create type 2024-10-20 20:15:12 +08:00
ljw
de0523852a up readme 2024-10-20 19:40:49 +08:00
ba85790d8c Merge pull request #17 from Ogannesson/master
Add proxy option for Google Oauthon
2024-10-20 19:05:01 +08:00
Oganneson
0091a9dd7f 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
e142cc00c6 add conn log 2024-10-18 15:05:58 +08:00
ljw
488fedf784 fix write when heartbeat #14 2024-10-17 10:24:02 +08:00
ljw
ceb65cd391 fix write when heartbeat #14 2024-10-16 21:32:55 +08:00
ljw
72c196c6f9 fix pc add #13 2024-10-16 10:10:20 +08:00
ljw
28f047ec79 fix pc add #13 2024-10-16 09:28:39 +08:00
ljw
7e0cc33c91 Revert "add webclient v2 preview"
This reverts commit e269341d51.
2024-10-15 16:35:34 +08:00
ljw
05a2961814 Revert "up readme"
This reverts commit dc8af3a460.
2024-10-15 16:35:34 +08:00
ljw
dc8af3a460 up readme 2024-10-15 16:13:42 +08:00
ljw
e269341d51 add webclient v2 preview 2024-10-15 16:11:40 +08:00
ljw
ebe5bc4c3a fix docs 2024-10-15 14:51:19 +08:00
ljw
b4965e8885 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
0eb733cc33 up readme 2024-10-12 10:02:04 +08:00
ljw
1e41784bb9 Revert "Revert "up readme""
This reverts commit 3d4e068266.
2024-10-11 22:45:35 +08:00
ljw
3d4e068266 Revert "up readme"
This reverts commit abc0e9d907.
2024-10-11 22:06:20 +08:00
ljw
abc0e9d907 up readme 2024-10-11 21:39:58 +08:00
ljw
5d361d05cc up readme 2024-10-11 10:11:42 +08:00
ljw
823c052d6d up readme 2024-10-10 13:00:29 +08:00
ljw
ebe39af682 fix readme 2024-10-10 12:40:54 +08:00
ljw
8b266d4fee up gorm logger & add share to guest by web client 2024-10-09 15:53:08 +08:00
ljw
2ab61512cf build tag 2024-09-29 12:47:04 +08:00
ljw
ee679d09f7 build default push to docker 2024-09-29 12:39:33 +08:00
ljw
71c44a501b fix build 2024-09-29 12:23:34 +08:00
f8a672070f Merge pull request #5 from gigaion/new-build-1
build.yml - Add GHCR & Dynamic Inputs
2024-09-29 12:12:17 +08:00
ljw
609dfaeea0 upgrade: init by i18n
add: batch delete peer
add: batch peer to addressbook
2024-09-29 11:53:58 +08:00
Gigaion
71e3529c2f build.yml - Add GHCR & Dynamic Inputs
build.yml - Add GHCR & Dynamic Inputs
2024-09-28 13:30:44 -07:00
ljw
61fc53858b test 2024-09-28 11:01:23 +08:00
ljw
a843748f60 test 2024-09-28 10:12:56 +08:00
ljw
c266f57a46 test 2024-09-28 10:05:04 +08:00
ljw
86f22e2666 test 2024-09-28 09:57:32 +08:00
ljw
c42e865784 fix build docker image 2024-09-28 09:39:26 +08:00
ljw
5ad31679a8 fix build docker image 2024-09-28 09:33:59 +08:00
ljw
56c84c854a up build 2024-09-27 22:08:36 +08:00
ljw
8118107f0f up build 2024-09-27 22:06:04 +08:00
ljw
d3aebec722 up build 2024-09-27 22:03:06 +08:00
ljw
808aa47856 up build 2024-09-27 21:48:48 +08:00
ljw
90f8c6b4e6 up build 2024-09-27 21:37:12 +08:00
ljw
5de0b522f1 up build 2024-09-27 21:29:19 +08:00
ljw
98e9828e1f up build docker echo manifest 2024-09-27 21:20:54 +08:00
ljw
71ff7ebd42 up build docker 2024-09-27 20:10:17 +08:00
ljw
bd084ced52 up build docker 2024-09-27 20:06:49 +08:00
ljw
cab04765f0 up build docker 2024-09-27 19:40:48 +08:00
ljw
5deaaa8a00 up build docker 2024-09-27 18:49:22 +08:00
ljw
58d39c7aa3 up build docker 2024-09-27 18:48:09 +08:00
ljw
87bec98a7f up build docker 2024-09-27 17:49:17 +08:00
ljw
74662ae58b up build docker 2024-09-27 17:32:25 +08:00
ljw
814bf48a54 up build docker 2024-09-27 17:26:40 +08:00
ljw
a1dba6c564 up build docker 2024-09-27 17:25:30 +08:00
ljw
418b182641 up build docker 2024-09-27 17:24:25 +08:00
ljw
bcbbf1099d up build docker 2024-09-27 17:15:06 +08:00
ljw
85c8377240 up build docker 2024-09-27 16:52:02 +08:00
ljw
264ae2dffa up build docker 2024-09-27 16:07:06 +08:00
ljw
b069e7876d up build docker 2024-09-27 15:59:12 +08:00
ljw
41670b0d00 up build docker 2024-09-27 15:31:43 +08:00
ljw
5bf8e176aa up build docker 2024-09-27 15:23:19 +08:00
ljw
9787ca0895 up build docker 2024-09-27 15:18:45 +08:00
ljw
04098cb6e1 up build docker 2024-09-27 14:58:17 +08:00
ljw
6411be66ba up build docker 2024-09-27 14:54:11 +08:00
ljw
0d3fd56c5e up build docker 2024-09-27 14:46:39 +08:00
ljw
9e7908c035 up build docker 2024-09-27 14:35:43 +08:00
ljw
08c7647c10 up build docker 2024-09-27 14:29:55 +08:00
ljw
cbbc8259e8 up build docker 2024-09-27 14:24:17 +08:00
ljw
aff735335b up build docker 2024-09-27 14:19:56 +08:00
ljw
47f1043cc2 up build docker 2024-09-27 14:18:36 +08:00
ljw
871bb328f0 up build docker 2024-09-27 14:16:59 +08:00
ljw
d18d8c0e3f up build docker 2024-09-27 14:10:50 +08:00
ljw
db7c43353d up release_arm64.yml 2024-09-27 10:43:48 +08:00
ljw
1847697ceb up release_arm64.yml 2024-09-27 10:38:14 +08:00
ljw
f5ffc433f7 up docker_arm64.yml 2024-09-26 14:36:15 +08:00
ljw
fe36f7b2f0 up docker_arm64.yml 2024-09-26 14:33:01 +08:00
ljw
4f2216d023 up docker_arm64.yml 2024-09-26 14:26:15 +08:00
ljw
a214113de5 up docker_arm64.yml 2024-09-26 14:24:14 +08:00
ljw
ea9dc84b91 add docker_arm64.yml 2024-09-26 14:18:57 +08:00
ljw
cf6bd41319 up README 2024-09-26 13:38:43 +08:00
ljw
0ab1ff9e18 up README_EN.md 2024-09-26 13:21:17 +08:00
ljw
c7b0996443 test release_arm64.yml 2024-09-26 11:49:11 +08:00
ljw
62ebc66259 test release_arm64.yml 2024-09-26 11:46:17 +08:00
ljw
6ce6a95e07 fix Dockerfile 2024-09-26 09:29:02 +08:00
ljw
467b61fb20 add i18n 2024-09-25 22:42:36 +08:00
ljw
c99261c12d add i18n 2024-09-25 22:41:57 +08:00
ljw
c9d70584cc up readme 2024-09-24 21:16:03 +08:00
ljw
0a81b5b5f8 fix group peers 2024-09-24 19:35:20 +08:00
ljw
95a1bd6c21 up readme 2024-09-24 15:22:21 +08:00
ljw
8c2f6f0929 up docs 2024-09-24 15:16:07 +08:00
ljw
c5b43fae08 up readme 2024-09-24 15:15:26 +08:00
ljw
c485035e74 add personal apis 2024-09-24 14:43:27 +08:00
ljw
a161e3ba10 up web client can get pwd if exist 2024-09-24 10:15:04 +08:00
ljw
5b5943b287 add docker.yml 2024-09-23 14:43:43 +08:00
ljw
69ad1b2e80 up README.md 2024-09-23 10:21:43 +08:00
ljw
01aad9bb15 up release.yml 2024-09-23 10:13:44 +08:00
ljw
b4b246def5 up release.yml 2024-09-23 10:10:29 +08:00
ljw
752d376dd7 up test.yml 2024-09-23 10:07:10 +08:00
ljw
601e1de3b3 up test.yml 2024-09-22 21:40:17 +08:00
ljw
758f1fb1c9 up test.yml 2024-09-22 21:28:43 +08:00
ljw
d394931fce up test.yml 2024-09-22 20:30:12 +08:00
ljw
192b3b11a9 up test.yml 2024-09-22 19:46:37 +08:00
ljw
4ec08cf03a up test.yml 2024-09-22 17:58:42 +08:00
cb1a06e4a9 Update test.yml 2024-09-21 22:49:06 +08:00
67b4678b2d Update test.yml 2024-09-21 21:55:36 +08:00
8d390dae50 Update test.yml 2024-09-21 21:44:43 +08:00
a4b40d746b Update go.yml 2024-09-21 21:43:31 +08:00
ddfd9f7923 Update test.yml 2024-09-21 21:40:36 +08:00
521e2c72f5 Create test.yml 2024-09-21 21:35:55 +08:00
79aa573e0f Update go.yml 2024-09-21 21:17:03 +08:00
5b6e56a8ec Create go.yml 2024-09-21 21:13:54 +08:00
163 changed files with 1665 additions and 221151 deletions

View File

@@ -66,7 +66,7 @@ jobs:
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.22' # 选择 Go 版本
go-version: '1.23' # 选择 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.cc/aarch64-linux-musl-cross.tgz
wget https://musl.ljw.red/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.cc/armv7l-linux-musleabihf-cross.tgz
wget https://musl.ljw.red/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,6 +147,7 @@ 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}}

View File

@@ -61,7 +61,7 @@ jobs:
- name: Set up Go environment
uses: actions/setup-go@v4
with:
go-version: '1.22' # 选择 Go 版本
go-version: '1.23' # 选择 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.cc/aarch64-linux-musl-cross.tgz
wget https://musl.ljw.red/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.cc/armv7l-linux-musleabihf-cross.tgz
wget https://musl.ljw.red/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

2
.gitignore vendored
View File

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

View File

@@ -42,11 +42,11 @@ RUN if [ "$COUNTRY" = "CN" ] ; then \
fi && \
apk update && apk add --no-cache git
ARG FREONTEND_GIT_REPO=https://github.com/lejianwen/rustdesk-api-web.git
ARG FRONTEND_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 $FREONTEND_GIT_REPO .
RUN git clone -b $FRONTEND_GIT_BRANCH $FRONTEND_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"]

View File

@@ -2,7 +2,8 @@
[English Doc](README_EN.md)
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web Admin 和 Web 客户端。RustDesk 是一个远程桌面软件,提供了自托管的解决方案。
本项目使用 Go 实现了 RustDesk 的 API并包含了 Web Admin 和 Web 客户端。
<div align=center>
<img src="https://img.shields.io/badge/golang-1.22-blue"/>
@@ -13,6 +14,14 @@
<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
@@ -45,7 +54,6 @@
- 自动获取ID服务器和KEY
- 自动获取地址簿
- 游客通过临时分享链接直接远程到设备
- v2 Preview
- CLI
- 重置管理员密码
@@ -94,8 +102,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/oauth/callback`
,比如`http://127.0.0.1:21114/api/oauth/callback`
- `Authorization callback URL`填写`http://<your server[:port]>/api/oidc/callback`
,比如`http://127.0.0.1:21114/api/oidc/callback`
7. 登录日志
8. 链接日志
9. 文件传输日志
@@ -118,9 +126,6 @@
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。
@@ -163,6 +168,9 @@
| 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` |
| -----ADMIN配置----- | ---------- | ---------- |
| RUSTDESK_API_ADMIN_TITLE | 后台标题 | `RustDesk Api Admin` |
| RUSTDESK_API_ADMIN_HELLO | 后台欢迎语,可以使用`html` | |
@@ -179,6 +187,7 @@
| 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 |
@@ -252,6 +261,12 @@
#或者使用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`
目录下生成对应的可执行文件。直接运行编译后的可执行文件即可。
@@ -316,3 +331,5 @@
</a>
## 感谢你的支持!如果这个项目对你有帮助,请点个⭐️鼓励一下,谢谢!
[lejianwen/rustdesk-server]: https://github.com/lejianwen/rustdesk-server

View File

@@ -12,6 +12,13 @@ desktop software that provides self-hosted solutions.
<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
@@ -94,8 +101,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/oauth/callback`,
e.g., `http://127.0.0.1:21114/api/oauth/callback`.
- Set the `Authorization callback URL` to `http://<your server[:port]>/api/oidc/callback`,
e.g., `http://127.0.0.1:21114/api/oidc/callback`.
7. Login logs
8. Connection logs
@@ -118,9 +125,6 @@ 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.
@@ -162,6 +166,9 @@ The table below does not list all configurations. Please refer to the configurat
| 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` | |
@@ -178,6 +185,7 @@ The table below does not list all configurations. Please refer to the configurat
| 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 |
@@ -248,10 +256,17 @@ 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
```
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
> ```
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
@@ -314,4 +329,7 @@ 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!
## 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

View File

@@ -1,6 +1,11 @@
package main
import (
"fmt"
"os"
"strconv"
"time"
"github.com/go-redis/redis/v8"
"github.com/lejianwen/rustdesk-api/v2/config"
"github.com/lejianwen/rustdesk-api/v2/global"
@@ -16,10 +21,10 @@ import (
"github.com/lejianwen/rustdesk-api/v2/utils"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/spf13/cobra"
"os"
"strconv"
)
const DatabaseVersion = 265
// @title 管理系统API
// @version 1.0
// @description 接口
@@ -139,18 +144,41 @@ func InitGlobal() {
}
//gorm
if global.Config.Gorm.Type == config.TypeMysql {
dns := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/" + global.Config.Mysql.Dbname + "?charset=utf8mb4&parseTime=True&loc=Local"
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,
)
global.DB = orm.NewMysql(&orm.MysqlConfig{
Dns: dns,
Dsn: dsn,
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)
}
//validator
@@ -175,10 +203,18 @@ func InitGlobal() {
//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 := 262
version := DatabaseVersion
db := global.DB
@@ -188,11 +224,17 @@ func DatabaseAutoUpdate() {
if dbName == "" {
dbName = global.Config.Mysql.Dbname
// 移除 DSN 中的数据库名称,以便初始连接时不指定数据库
dsnWithoutDB := global.Config.Mysql.Username + ":" + global.Config.Mysql.Password + "@(" + global.Config.Mysql.Addr + ")/?charset=utf8mb4&parseTime=True&loc=Local"
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,
"",
)
//新链接
dbWithoutDB := orm.NewMysql(&orm.MysqlConfig{
Dns: dsnWithoutDB,
})
Dsn: dsnWithoutDB,
}, global.Logger)
// 获取底层的 *sql.DB 对象,并确保在程序退出时关闭连接
sqlDBWithoutDB, err := dbWithoutDB.DB()
if err != nil {
@@ -304,7 +346,11 @@ func Migrate(version uint) {
// 生成随机密码
pwd := utils.RandomString(8)
global.Logger.Info("Admin Password Is: ", pwd)
admin.Password = service.AllService.UserService.EncryptPassword(pwd)
var err error
admin.Password, err = utils.EncryptPassword(pwd)
if err != nil {
global.Logger.Fatalf("failed to generate admin password: %v", err)
}
global.DB.Create(admin)
}

View File

@@ -2,14 +2,21 @@ 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
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
@@ -24,6 +31,17 @@ 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"
@@ -63,4 +81,4 @@ 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.

View File

@@ -14,33 +14,48 @@ const (
)
type App struct {
WebClient int `mapstructure:"web-client"`
Register bool `mapstructure:"register"`
ShowSwagger int `mapstructure:"show-swagger"`
TokenExpire time.Duration `mapstructure:"token-expire"`
WebSso bool `mapstructure:"web-sso"`
DisablePwdLogin bool `mapstructure:"disable-pwd-login"`
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"`
}
type Admin struct {
Title string `mapstructure:"title"`
Hello string `mapstructure:"hello"`
HelloFile string `mapstructure:"hello-file"`
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"`
}
type Config struct {
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
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
}
}
// Init 初始化配置
@@ -77,7 +92,7 @@ func Init(rowVal *Config, path string) *viper.Viper {
panic(fmt.Errorf("Fatal error config: %s \n", err))
}
rowVal.Rustdesk.LoadKeyFile()
rowVal.Rustdesk.ParsePort()
rowVal.Admin.Init()
return v
}

View File

@@ -1,8 +1,9 @@
package config
const (
TypeSqlite = "sqlite"
TypeMysql = "mysql"
TypeSqlite = "sqlite"
TypeMysql = "mysql"
TypePostgresql = "postgresql"
)
type Gorm struct {
@@ -16,4 +17,15 @@ 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

@@ -11,6 +11,7 @@ type LdapUser struct {
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
}
// type LdapGroup struct {

View File

@@ -3,18 +3,20 @@ 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"`
RedirectUrl string `mapstructure:"redirect-url"`
}
type LinuxdoOauth struct {
ClientId string `mapstructure:"client-id"`
ClientSecret string `mapstructure:"client-secret"`
}

View File

@@ -2,8 +2,6 @@ package config
import (
"os"
"strconv"
"strings"
)
const (
@@ -40,19 +38,3 @@ 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])
}
}

0
data/.gitkeep Normal file
View File

View File

@@ -5,7 +5,7 @@ services:
dockerfile: Dockerfile.dev
args:
COUNTRY: CN
FREONTEND_GIT_REPO: https://github.com/lejianwen/rustdesk-api-web.git
FRONTEND_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

@@ -5569,8 +5569,7 @@ const docTemplateadmin = `{
"required": [
"client_id",
"client_secret",
"oauth_type",
"redirect_url"
"oauth_type"
],
"properties": {
"auto_register": {
@@ -5600,9 +5599,6 @@ const docTemplateadmin = `{
"pkce_method": {
"type": "string"
},
"redirect_url": {
"type": "string"
},
"scopes": {
"type": "string"
}
@@ -5828,6 +5824,9 @@ const docTemplateadmin = `{
"captcha": {
"type": "string"
},
"captcha_id": {
"type": "string"
},
"password": {
"type": "string"
},
@@ -6293,9 +6292,6 @@ const docTemplateadmin = `{
"pkce_method": {
"type": "string"
},
"redirect_url": {
"type": "string"
},
"scopes": {
"type": "string"
},

View File

@@ -5562,8 +5562,7 @@
"required": [
"client_id",
"client_secret",
"oauth_type",
"redirect_url"
"oauth_type"
],
"properties": {
"auto_register": {
@@ -5593,9 +5592,6 @@
"pkce_method": {
"type": "string"
},
"redirect_url": {
"type": "string"
},
"scopes": {
"type": "string"
}
@@ -5821,6 +5817,9 @@
"captcha": {
"type": "string"
},
"captcha_id": {
"type": "string"
},
"password": {
"type": "string"
},
@@ -6286,9 +6285,6 @@
"pkce_method": {
"type": "string"
},
"redirect_url": {
"type": "string"
},
"scopes": {
"type": "string"
},
@@ -6592,4 +6588,4 @@
"in": "header"
}
}
}
}

View File

@@ -143,15 +143,12 @@ definitions:
type: boolean
pkce_method:
type: string
redirect_url:
type: string
scopes:
type: string
required:
- client_id
- client_secret
- oauth_type
- redirect_url
type: object
admin.PeerBatchDeleteForm:
properties:
@@ -297,6 +294,8 @@ definitions:
properties:
captcha:
type: string
captcha_id:
type: string
password:
type: string
platform:
@@ -609,8 +608,6 @@ definitions:
type: boolean
pkce_method:
type: string
redirect_url:
type: string
scopes:
type: string
updated_at:

View File

@@ -954,35 +954,6 @@ 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",
@@ -1041,6 +1012,35 @@ 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 +1208,7 @@ const docTemplateapi = `{
"application/json"
],
"tags": [
"地址"
"System"
],
"summary": "提交系统信息",
"parameters": [
@@ -1238,6 +1238,35 @@ 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

@@ -947,35 +947,6 @@
}
}
},
"/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",
@@ -1034,6 +1005,35 @@
}
}
},
"/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 +1201,7 @@
"application/json"
],
"tags": [
"地址"
"System"
],
"summary": "提交系统信息",
"parameters": [
@@ -1231,6 +1231,35 @@
}
}
},
"/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

@@ -792,25 +792,6 @@ 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:
@@ -849,6 +830,25 @@ 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,7 +973,26 @@ 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

@@ -10,6 +10,7 @@ import (
"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"
@@ -31,8 +32,9 @@ 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
Oss *upload.Oss
Jwt *jwt.Jwt
Lock lock.Locker
Localizer func(lang string) *i18n.Localizer
LoginLimiter *utils.LoginLimiter
)

25
go.mod
View File

@@ -1,19 +1,23 @@
module github.com/lejianwen/rustdesk-api/v2
go 1.22
go 1.23
toolchain go1.23.10
require (
github.com/BurntSushi/toml v1.3.2
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/fsnotify/fsnotify v1.5.1
github.com/coreos/go-oidc/v3 v3.12.0
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-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
@@ -21,11 +25,13 @@ 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
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.7
gorm.io/gorm v1.25.10
)
require (
@@ -36,13 +42,12 @@ require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/coreos/go-oidc/v3 v3.12.0 // 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
@@ -52,6 +57,10 @@ 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
@@ -65,9 +74,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
@@ -76,11 +85,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.33.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.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/tools v0.26.0 // 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

@@ -78,11 +78,13 @@ func (co *Config) AdminConfig(c *gin.Context) {
}
hello := global.Config.Admin.Hello
helloFile := global.Config.Admin.HelloFile
if helloFile != "" {
b, err := os.ReadFile(helloFile)
if err == nil && len(b) > 0 {
hello = string(b)
if hello == "" {
helloFile := global.Config.Admin.HelloFile
if helloFile != "" {
b, err := os.ReadFile(helloFile)
if err == nil && len(b) > 0 {
hello = string(b)
}
}
}

View File

@@ -38,7 +38,7 @@ func (f *File) Notify(c *gin.Context) {
res := global.Oss.Verify(c.Request)
if !res {
response.Fail(c, 101, "权限错误")
response.Fail(c, 101, response.TranslateMsg(c, "NoAccess"))
return
}
fm := &FileBack{}

View File

@@ -2,6 +2,7 @@ package admin
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/controller/api"
@@ -11,135 +12,11 @@ import (
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 登录
@@ -156,10 +33,16 @@ func (ct *Login) Login(c *gin.Context) {
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
@@ -167,14 +50,15 @@ 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 loginLimiter.NeedsCaptcha(clientIp) {
if f.Captcha == "" || !loginLimiter.VerifyCaptcha(clientIp, f.Captcha) {
if needCaptcha {
if f.CaptchaId == "" || f.Captcha == "" || !loginLimiter.VerifyCaptcha(f.CaptchaId, f.Captcha) {
response.Fail(c, 101, response.TranslateMsg(c, "CaptchaError"))
return
}
@@ -184,17 +68,19 @@ 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.RecordFailure(clientIp)
if loginLimiter.NeedsCaptcha(clientIp) {
loginLimiter.RemoveCaptcha(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"))
}
response.Fail(c, 101, response.TranslateMsg(c, "UsernameOrPasswordError"))
return
}
if !service.AllService.UserService.CheckUserEnable(u) {
if loginLimiter.NeedsCaptcha(clientIp) {
loginLimiter.RemoveCaptcha(clientIp)
if needCaptcha {
response.Fail(c, 110, response.TranslateMsg(c, "UserDisabled"))
return
}
response.Fail(c, 101, response.TranslateMsg(c, "UserDisabled"))
return
@@ -209,23 +95,37 @@ func (ct *Login) Login(c *gin.Context) {
Platform: f.Platform,
})
// 成功清除记录
loginLimiter.RemoveRecord(clientIp)
// 清理过期记录
go loginLimiter.CleanupExpired()
// 登录成功清除登录限制
loginLimiter.RemoveAttempts(clientIp)
responseLoginSuccess(c, u, ut.Token)
}
func (ct *Login) Captcha(c *gin.Context) {
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
if !loginLimiter.NeedsCaptcha(clientIp) {
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
if banned {
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
return
}
if !needCaptcha {
response.Fail(c, 101, response.TranslateMsg(c, "NoCaptchaRequired"))
return
}
captcha := loginLimiter.GenerateCaptcha(clientIp)
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
}
response.Success(c, gin.H{
"captcha": captcha,
"captcha": gin.H{
"id": captcha.Id,
"b64": b64,
},
})
}
@@ -257,12 +157,20 @@ func (ct *Login) Logout(c *gin.Context) {
// @Failure 500 {object} response.ErrorResponse
// @Router /admin/login-options [post]
func (ct *Login) LoginOptions(c *gin.Context) {
ip := c.ClientIP()
loginLimiter := global.LoginLimiter
clientIp := c.ClientIP()
banned, needCaptcha := loginLimiter.CheckSecurityStatus(clientIp)
if banned {
response.Fail(c, 101, response.TranslateMsg(c, "LoginBanned"))
return
}
ops := service.AllService.OauthService.GetOauthProviders()
response.Success(c, gin.H{
"ops": ops,
"register": global.Config.App.Register,
"need_captcha": loginLimiter.NeedsCaptcha(ip),
"need_captcha": needCaptcha,
"disable_pwd": global.Config.App.DisablePwdLogin,
"auto_oidc": global.Config.App.DisablePwdLogin && len(ops) == 1,
})
}

View File

@@ -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,13 +1,14 @@
package admin
import (
"strconv"
"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 {
@@ -68,16 +69,16 @@ func (o *Oauth) Confirm(c *gin.Context) {
j := &adminReq.OauthConfirmForm{}
err := c.ShouldBindJSON(j)
if err != nil {
response.Fail(c, 101, "参数错误"+err.Error())
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
return
}
if j.Code == "" {
response.Fail(c, 101, "参数错误: code 不存在")
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
v := service.AllService.OauthService.GetOauthCache(j.Code)
if v == nil {
response.Fail(c, 101, "授权已过期")
response.Fail(c, 101, response.TranslateMsg(c, "OauthExpired"))
return
}
u := service.AllService.UserService.CurUser(c)

View File

@@ -114,6 +114,9 @@ func (ct *Peer) List(c *gin.Context) {
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

@@ -119,7 +119,16 @@ func (r *Rustdesk) SendCmd(c *gin.Context) {
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
return
}
res, err := service.AllService.ServerCmdService.SendCmd(rc.Target, rc.Cmd, rc.Option)
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)
if err != nil {
response.Fail(c, 101, err.Error())
return

View File

@@ -8,6 +8,7 @@ import (
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"
)
@@ -243,11 +244,10 @@ func (ct *User) ChangeCurPwd(c *gin.Context) {
return
}
u := service.AllService.UserService.CurUser(c)
// If the password is not empty, the old password is verified
// otherwise, the old password is not verified
// Verify the old password only when the account already has one set
if !service.AllService.UserService.IsPasswordEmptyByUser(u) {
oldPwd := service.AllService.UserService.EncryptPassword(f.OldPassword)
if u.Password != oldPwd {
ok, _, err := utils.VerifyPassword(u.Password, f.OldPassword)
if err != nil || !ok {
response.Fail(c, 101, response.TranslateMsg(c, "OldPasswordError"))
return
}
@@ -320,11 +320,22 @@ func (ct *User) Register(c *gin.Context) {
response.Fail(c, 101, errList[0])
return
}
u := service.AllService.UserService.Register(f.Username, f.Email, f.Password)
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)
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

@@ -49,7 +49,7 @@ func (i *Index) Heartbeat(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{})
return
}
peer := service.AllService.PeerService.FindByUuid(info.Uuid)
peer := service.AllService.PeerService.FindById(info.Id)
if peer == nil || peer.RowId == 0 {
c.JSON(http.StatusOK, gin.H{})
return

View File

@@ -31,10 +31,16 @@ func (l *Login) Login(c *gin.Context) {
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
@@ -42,6 +48,7 @@ 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
@@ -50,6 +57,7 @@ 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,6 +1,8 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/lejianwen/rustdesk-api/v2/global"
"github.com/lejianwen/rustdesk-api/v2/http/request/api"
@@ -10,7 +12,6 @@ import (
"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 {
@@ -79,7 +80,8 @@ func (o *Oauth) OidcAuthQueryPre(c *gin.Context) (*model.User, *model.UserToken)
// 如果 UserId 为 0说明还在授权中
if v.UserId == 0 {
c.JSON(http.StatusOK, gin.H{"message": "Authorization in progress, please login and bind"})
//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"})
return nil, nil
}
@@ -142,7 +144,7 @@ func (o *Oauth) OidcAuthQuery(c *gin.Context) {
// @Produce json
// @Success 200 {object} apiResp.LoginRes
// @Failure 500 {object} response.ErrorResponse
// @Router /oauth/callback [get]
// @Router /oidc/callback [get]
func (o *Oauth) OauthCallback(c *gin.Context) {
state := c.Query("state")
if state == "" {
@@ -225,8 +227,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
if !*oauthConfig.AutoRegister {
//c.String(http.StatusInternalServerError, "还未绑定用户,请先绑定")
oauthCache.UpdateFromOauthUser(oauthUser)
url := global.Config.Rustdesk.ApiServer + "/_admin/#/oauth/bind/" + cacheKey
c.Redirect(http.StatusFound, url)
c.Redirect(http.StatusFound, "/_admin/#/oauth/bind/"+cacheKey)
return
}
@@ -251,8 +252,7 @@ func (o *Oauth) OauthCallback(c *gin.Context) {
Type: model.LoginLogTypeOauth,
Platform: oauthService.DeviceOs,
})*/
url := global.Config.Rustdesk.ApiServer + "/_admin/#/"
c.Redirect(http.StatusFound, url)
c.Redirect(http.StatusFound, "/_admin/#/")
return
}
c.HTML(http.StatusOK, "oauth_success.html", gin.H{

View File

@@ -1,6 +1,7 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
requstform "github.com/lejianwen/rustdesk-api/v2/http/request/api"
@@ -13,7 +14,7 @@ type Peer struct {
}
// SysInfo
// @Tags 地址
// @Tags System
// @Summary 提交系统信息
// @Description 提交系统信息
// @Accept json
@@ -30,10 +31,10 @@ func (p *Peer) SysInfo(c *gin.Context) {
return
}
fpe := f.ToPeer()
pe := service.AllService.PeerService.FindByUuid(f.Uuid)
pe := service.AllService.PeerService.FindById(f.Id)
if pe.RowId == 0 {
pe = f.ToPeer()
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
err = service.AllService.PeerService.Create(pe)
if err != nil {
response.Error(c, response.TranslateMsg(c, "OperationFailed")+err.Error())
@@ -41,7 +42,7 @@ func (p *Peer) SysInfo(c *gin.Context) {
}
} else {
if pe.UserId == 0 {
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid)
pe.UserId = service.AllService.UserService.FindLatestUserIdFromLoginLogByUuid(pe.Uuid, pe.Id)
}
fpe.RowId = pe.RowId
fpe.UserId = pe.UserId
@@ -57,8 +58,19 @@ 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

@@ -33,7 +33,7 @@ func ApiInit() {
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "404 not found")
})
g.Use(middleware.Logger(), gin.Recovery())
g.Use(middleware.Logger(), middleware.Limiter(), gin.Recovery())
router.WebInit(g)
router.Init(g)
router.ApiInit(g)

View File

@@ -13,13 +13,13 @@ func BackendUserAuth() gin.HandlerFunc {
//测试先关闭
token := c.GetHeader("api-token")
if token == "" {
response.Fail(c, 403, "请先登录")
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
c.Abort()
return
}
user, ut := service.AllService.UserService.InfoByAccessToken(token)
if user.Id == 0 {
response.Fail(c, 403, "请先登录")
response.Fail(c, 403, response.TranslateMsg(c, "NeedLogin"))
c.Abort()
return
}

View File

@@ -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.Fail(c, 403, response.TranslateMsg(c, "NoAccess"))
c.Abort()
return
}

View File

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

View File

@@ -0,0 +1,22 @@
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,10 +1,11 @@
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:"验证码"`
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"`
}
type LoginLogQuery struct {

View File

@@ -22,7 +22,6 @@ type OauthForm struct {
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"`
PkceEnable *bool `json:"pkce_enable"`
PkceMethod string `json:"pkce_method"`
@@ -34,7 +33,6 @@ func (of *OauthForm) ToOauth() *model.Oauth {
OauthType: of.OauthType,
ClientId: of.ClientId,
ClientSecret: of.ClientSecret,
RedirectUrl: of.RedirectUrl,
AutoRegister: of.AutoRegister,
Issuer: of.Issuer,
Scopes: of.Scopes,

View File

@@ -13,6 +13,7 @@ type PeerForm struct {
Uuid string `json:"uuid"`
Version string `json:"version"`
GroupId uint `json:"group_id"`
Alias string `json:"alias"`
}
type PeerBatchDeleteForm struct {
@@ -32,6 +33,7 @@ func (f *PeerForm) ToPeer() *model.Peer {
Uuid: f.Uuid,
Version: f.Version,
GroupId: f.GroupId,
Alias: f.Alias,
}
}
@@ -43,6 +45,7 @@ type PeerQuery struct {
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

@@ -14,6 +14,7 @@ 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 {
@@ -25,6 +26,7 @@ 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 {
@@ -37,6 +39,7 @@ 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

@@ -49,6 +49,10 @@ func ApiInit(g *gin.Engine) {
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{}

View File

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

45
lib/orm/postgresql.go Normal file
View File

@@ -0,0 +1,45 @@
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

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

View File

@@ -14,6 +14,7 @@ const (
OauthTypeGoogle string = "google"
OauthTypeOidc string = "oidc"
OauthTypeWebauth string = "webauth"
OauthTypeLinuxdo string = "linuxdo"
PKCEMethodS256 string = "S256"
PKCEMethodPlain string = "plain"
)
@@ -21,7 +22,7 @@ const (
// Validate the oauth type
func ValidateOauthType(oauthType string) error {
switch oauthType {
case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth:
case OauthTypeGithub, OauthTypeGoogle, OauthTypeOidc, OauthTypeWebauth, OauthTypeLinuxdo:
return nil
default:
return errors.New("invalid Oauth type")
@@ -29,8 +30,9 @@ func ValidateOauthType(oauthType string) error {
}
const (
UserEndpointGithub string = "https://api.github.com/user"
IssuerGoogle string = "https://accounts.google.com"
UserEndpointGithub string = "https://api.github.com/user"
UserEndpointLinuxdo string = "https://connect.linux.do/api/user"
IssuerGoogle string = "https://accounts.google.com"
)
type Oauth struct {
@@ -39,12 +41,12 @@ 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"`
PkceEnable *bool `json:"pkce_enable"`
PkceMethod string `json:"pkce_method"`
TimeModel
}
@@ -60,6 +62,8 @@ 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)
@@ -152,6 +156,24 @@ 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

@@ -15,6 +15,7 @@ type Peer struct {
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,6 +11,7 @@ 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

@@ -33,6 +33,11 @@ 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."
@@ -142,4 +147,14 @@ other = "Password login disabled."
[CannotShareToSelf]
description = "Cannot share to self."
one = "Cannot share to self."
other = "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,6 +33,11 @@ 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."
@@ -151,4 +156,14 @@ 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."
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."

View File

@@ -33,6 +33,11 @@ description = "No access."
one = "Aucun d'access."
other = "Aucun d'access."
[NeedLogin]
description = "Need login."
one = "Veuillez d'abord vous connecter."
other = "Veuillez d'abord vous connecter."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Nom d'utilisateur ou de mot de passe incorrect."
@@ -151,4 +156,14 @@ other = "Connexion par mot de passe désactivée."
[CannotShareToSelf]
description = "Cannot share to self."
one = "Impossible de partager avec soi-même."
other = "Impossible de partager avec soi-même."
other = "Impossible de partager avec soi-même."
[Banned]
description = "Banned."
one = "Banni."
other = "Banni."
[RegisterSuccessWaitAdminConfirm]
description = "Register success wait admin confirm."
one = "Inscription réussie, veuillez attendre la confirmation de l'administrateur."
other = "Inscription réussie, veuillez attendre la confirmation de l'administrateur."

View File

@@ -33,6 +33,11 @@ description = "No access."
one = "접근할 수 없습니다."
other = "접근할 수 없습니다."
[NeedLogin]
description = "Need login."
one = "먼저 로그인해주세요."
other = "먼저 로그인해주세요."
[UsernameOrPasswordError]
description = "Username or password error."
one = "사용자 이름이나 비밀번호가 올바르지 않습니다."
@@ -145,4 +150,14 @@ other = "비밀번호 로그인이 비활성화되었습니다."
[CannotShareToSelf]
description = "Cannot share to self."
one = "자기 자신에게 공유할 수 없습니다."
other = "자기 자신에게 공유할 수 없습니다."
other = "자기 자신에게 공유할 수 없습니다."
[Banned]
description = "Banned."
one = "금지됨."
other = "금지됨."
[RegisterSuccessWaitAdminConfirm]
description = "Register success wait admin confirm."
one = "가입 성공, 관리자 확인 대기 중."
other = "가입 성공, 관리자 확인 대기 중."

View File

@@ -33,6 +33,11 @@ description = "No access."
one = "Нет доступа."
other = "Нет доступа."
[NeedLogin]
description = "Need login."
one = "Пожалуйста, войдите в систему."
other = "Пожалуйста, войдите в систему."
[UsernameOrPasswordError]
description = "Username or password error."
one = "Неправильное имя пользователя или пароль."
@@ -151,4 +156,14 @@ other = "Вход по паролю отключен."
[CannotShareToSelf]
description = "Cannot share to self."
one = "Нельзя поделиться с собой."
other = "Нельзя поделиться с собой."
other = "Нельзя поделиться с собой."
[Banned]
description = "Banned."
one = "Заблокировано."
other = "Заблокировано."
[RegisterSuccessWaitAdminConfirm]
description = "Register success wait admin confirm."
one = "Регистрация прошла успешно, ожидайте подтверждения администратора."
other = "Регистрация прошла успешно, ожидайте подтверждения администратора."

View File

@@ -33,6 +33,11 @@ description = "No access."
one = "无权限。"
other = "无权限。"
[NeedLogin]
description = "Need login."
one = "请先登录。"
other = "请先登录。"
[UsernameOrPasswordError]
description = "Username or password error."
one = "用户名或密码错误。"
@@ -144,4 +149,14 @@ other = "密码登录已禁用。"
[CannotShareToSelf]
description = "Cannot share to self."
one = "不能共享给自己。"
other = "不能共享给自己。"
other = "不能共享给自己。"
[Banned]
description = "Banned."
one = "已被封禁。"
other = "已被封禁。"
[RegisterSuccessWaitAdminConfirm]
description = "Register success, wait for admin confirm."
one = "注册成功,请等待管理员审核。"
other = "注册成功,请等待管理员审核。"

View File

@@ -5,8 +5,8 @@ other = "測試2 {{.P0}}"
[ParamsError]
description = "Params validation failed."
one = "引數錯誤。"
other = "引數錯誤。"
one = "參數驗證失敗。"
other = "參數驗證失敗。"
[OperationFailed]
description = "OperationFailed."
@@ -20,18 +20,23 @@ other = "操作成功。"
[ItemExists]
description = "Item already exists."
one = "資料已存在。"
other = "資料已存在。"
one = "項目已存在。"
other = "項目已存在。"
[ItemNotFound]
description = "Item not found."
one = "資料不存在。"
other = "資料不存在。"
one = "找不到項目。"
other = "找不到項目。"
[NoAccess]
description = "No access."
one = "無許可權。"
other = "無許可權。"
one = "無權限存取。"
other = "無權限存取。"
[NeedLogin]
description = "Need login."
one = "請先登入。"
other = "請先登入。"
[UsernameOrPasswordError]
description = "Username or password error."
@@ -45,24 +50,23 @@ other = "系統錯誤。"
[ConfigNotFound]
description = "Config not found."
one = "配置不存在。"
other = "配置不存在。"
one = "找不到設定。"
other = "找不到設定。"
#授權過期
[OauthExpired]
description = "Oauth expired."
one = "授權過期,請重新授權。"
other = "授權過期,請重新授權。"
one = "OAuth 已過期,請重。"
other = "OAuth 已過期,請重。"
[OauthFailed]
description = "Oauth failed."
one = "授權失敗。"
other = "授權失敗。"
one = "OAuth 失敗。"
other = "OAuth 失敗。"
[OauthHasBindOtherUser]
description = "Oauth has bind other user."
one = "授權已繫結其他使用者。"
other = "授權已繫結其他使用者。"
one = "OAuth 已綁定其他使用者。"
other = "OAuth 已綁定其他使用者。"
[ParamIsEmpty]
description = "Param is empty."
@@ -71,56 +75,64 @@ other = "{{.P0}} 為空。"
[BindFail]
description = "Bind fail."
one = "繫結失敗。"
other = "繫結失敗。"
one = "綁定失敗。"
other = "綁定失敗。"
[BindSuccess]
description = "Bind success."
one = "繫結成功。"
other = "繫結成功。"
one = "綁定成功。"
other = "綁定成功。"
[OauthHasBeenSuccess]
description = "Oauth has been success."
one = "授權已成功。"
other = "授權已成功。"
one = "OAuth 已成功。"
other = "OAuth 已成功。"
[OauthSuccess]
description = "Oauth success."
one = "授權成功。"
other = "授權成功。"
one = "OAuth 成功。"
other = "OAuth 成功。"
[OauthRegisterSuccess]
description = "Oauth register success."
one = "授權註冊成功。"
other = "授權註冊成功。"
one = "OAuth 註冊成功。"
other = "OAuth 註冊成功。"
[OauthRegisterFailed]
description = "Oauth register failed."
one = "授權註冊失敗。"
other = "授權註冊失敗。"
one = "OAuth 註冊失敗。"
other = "OAuth 註冊失敗。"
[GetOauthTokenError]
description = "Get oauth token error."
one = "獲取授權token失敗。"
other = "獲取授權token失敗。"
one = "取得 OAuth 權杖錯誤。"
other = "取得 OAuth 權杖錯誤。"
[GetOauthUserInfoError]
description = "Get oauth user info error."
one = "獲取授權使用者資訊失敗。"
other = "獲取授權使用者資訊失敗。"
one = "取得 OAuth 使用者資訊錯誤。"
other = "取得 OAuth 使用者資訊錯誤。"
[DecodeOauthUserInfoError]
description = "Decode oauth user info error."
one = "解析授權使用者資訊失敗。"
other = "解析授權使用者資訊失敗。"
one = "解析 OAuth 使用者資訊錯誤。"
other = "解析 OAuth 使用者資訊錯誤。"
[OldPasswordError]
description = "Old password error."
one = "舊密碼錯誤。"
other = "舊密碼錯誤。"
[DefaultGroup]
description = "Default group."
one = "預設組"
other = "預設組"
one = "預設組"
other = "預設組"
[ShareGroup]
description = "Share group."
one = "共享組"
other = "共享組"
one = "共享組"
other = "共享組"
[RegisterClosed]
description = "Register closed."
one = "註冊已關閉。"
@@ -138,10 +150,20 @@ other = "驗證碼錯誤。"
[PwdLoginDisabled]
description = "Password login disabled."
one = "密碼登錄已禁用。"
other = "密碼登錄已禁用。"
one = "密碼登入已停用。"
other = "密碼登入已停用。"
[CannotShareToSelf]
description = "Cannot share to self."
one = "無法享給自己。"
other = "無法享給自己。"
one = "無法享給自己。"
other = "無法享給自己。"
[Banned]
description = "Banned."
one = "已被禁用。"
other = "已被禁用。"
[RegisterSuccessWaitAdminConfirm]
description = "Register success, wait admin confirm."
one = "註冊成功,等待管理員確認。"
other = "註冊成功,等待管理員確認。"

View File

@@ -62,7 +62,7 @@
var title = 'OauthFailed'
var msg = '{{.message}}'
var btn = 'Close'
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthFailed"><\/script>');
document.writeln('<script src="/api/oidc/msg?lang=' + lang + '&msg=' + msg + '&title=OauthFailed"><\/script>');
</script>
</head>
<body>

View File

@@ -61,7 +61,7 @@
var title = 'OauthSuccess'
var msg = '{{.message}}'
var btn = 'Close'
document.writeln('<script src="/api/oauth/msg?lang=' + lang + '&msg=' + msg + '&title=OauthSuccess"><\/script>');
document.writeln('<script src="/api/oidc/msg?lang=' + lang + '&msg=' + msg + '&title=OauthSuccess"><\/script>');
</script>
</head>
<body>

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@
{"assets/actions.svg":["assets/actions.svg"],"assets/actions_mobile.svg":["assets/actions_mobile.svg"],"assets/address_book.ttf":["assets/address_book.ttf"],"assets/android.svg":["assets/android.svg"],"assets/arrow.svg":["assets/arrow.svg"],"assets/auth-apple.svg":["assets/auth-apple.svg"],"assets/auth-auth0.svg":["assets/auth-auth0.svg"],"assets/auth-azure.svg":["assets/auth-azure.svg"],"assets/auth-default.svg":["assets/auth-default.svg"],"assets/auth-facebook.svg":["assets/auth-facebook.svg"],"assets/auth-github.svg":["assets/auth-github.svg"],"assets/auth-gitlab.svg":["assets/auth-gitlab.svg"],"assets/auth-google.svg":["assets/auth-google.svg"],"assets/auth-okta.svg":["assets/auth-okta.svg"],"assets/call_end.svg":["assets/call_end.svg"],"assets/call_wait.svg":["assets/call_wait.svg"],"assets/chat.svg":["assets/chat.svg"],"assets/chat2.svg":["assets/chat2.svg"],"assets/checkbox-outline.svg":["assets/checkbox-outline.svg"],"assets/chevron_up_chevron_down.svg":["assets/chevron_up_chevron_down.svg"],"assets/close.svg":["assets/close.svg"],"assets/display.svg":["assets/display.svg"],"assets/dots.svg":["assets/dots.svg"],"assets/file.svg":["assets/file.svg"],"assets/file_transfer.svg":["assets/file_transfer.svg"],"assets/folder.svg":["assets/folder.svg"],"assets/folder_new.svg":["assets/folder_new.svg"],"assets/fullscreen.svg":["assets/fullscreen.svg"],"assets/fullscreen_exit.svg":["assets/fullscreen_exit.svg"],"assets/gestures.ttf":["assets/gestures.ttf"],"assets/home.svg":["assets/home.svg"],"assets/icon.svg":["assets/icon.svg"],"assets/insecure.svg":["assets/insecure.svg"],"assets/insecure_relay.svg":["assets/insecure_relay.svg"],"assets/kb_layout_iso.svg":["assets/kb_layout_iso.svg"],"assets/kb_layout_not_iso.svg":["assets/kb_layout_not_iso.svg"],"assets/keyboard.svg":["assets/keyboard.svg"],"assets/linux.svg":["assets/linux.svg"],"assets/mac.svg":["assets/mac.svg"],"assets/message_24dp_5F6368.svg":["assets/message_24dp_5F6368.svg"],"assets/peer_searchbar.ttf":["assets/peer_searchbar.ttf"],"assets/pinned.svg":["assets/pinned.svg"],"assets/rec.svg":["assets/rec.svg"],"assets/record_screen.svg":["assets/record_screen.svg"],"assets/refresh.svg":["assets/refresh.svg"],"assets/scam.png":["assets/scam.png"],"assets/screen.svg":["assets/screen.svg"],"assets/search.svg":["assets/search.svg"],"assets/secure.svg":["assets/secure.svg"],"assets/secure_relay.svg":["assets/secure_relay.svg"],"assets/tabbar.ttf":["assets/tabbar.ttf"],"assets/transfer.svg":["assets/transfer.svg"],"assets/trash.svg":["assets/trash.svg"],"assets/unpinned.svg":["assets/unpinned.svg"],"assets/voice_call.svg":["assets/voice_call.svg"],"assets/voice_call_waiting.svg":["assets/voice_call_waiting.svg"],"assets/win.svg":["assets/win.svg"],"packages/dash_chat_2/assets/placeholder.png":["packages/dash_chat_2/assets/placeholder.png"],"packages/dash_chat_2/assets/profile_placeholder.png":["packages/dash_chat_2/assets/profile_placeholder.png"],"packages/flex_color_picker/assets/opacity.png":["packages/flex_color_picker/assets/opacity.png"],"packages/wakelock_plus/assets/no_sleep.js":["packages/wakelock_plus/assets/no_sleep.js"],"packages/window_manager/images/ic_chrome_close.png":["packages/window_manager/images/ic_chrome_close.png"],"packages/window_manager/images/ic_chrome_maximize.png":["packages/window_manager/images/ic_chrome_maximize.png"],"packages/window_manager/images/ic_chrome_minimize.png":["packages/window_manager/images/ic_chrome_minimize.png"],"packages/window_manager/images/ic_chrome_unmaximize.png":["packages/window_manager/images/ic_chrome_unmaximize.png"]}

View File

@@ -1,58 +0,0 @@
[
{
"family": "MaterialIcons",
"fonts": [
{
"asset": "fonts/MaterialIcons-Regular.otf"
}
]
},
{
"family": "GestureIcons",
"fonts": [
{
"asset": "assets/gestures.ttf"
}
]
},
{
"family": "Tabbar",
"fonts": [
{
"asset": "assets/tabbar.ttf"
}
]
},
{
"family": "PeerSearchbar",
"fonts": [
{
"asset": "assets/peer_searchbar.ttf"
}
]
},
{
"family": "AddressBook",
"fonts": [
{
"asset": "assets/address_book.ttf"
}
]
},
{
"family": "DeviceGroup",
"fonts": [
{
"asset": "assets/device_group.ttf"
}
]
},
{
"family": "More",
"fonts": [
{
"asset": "assets/more.ttf"
}
]
}
]

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="496.063 521.772 32 32"><path fill="none" d="M496.063 521.772h32v32h-32v-32Z"/><path d="m513.817 535.858.87-8.052c.136-1.26-.279-1.399-.927-.31l-6.856 11.532c-.216.363-.049.658.374.658h3.031l-.87 8.052c-.136 1.26.279 1.399.927.309l6.856-11.531c.216-.363.048-.658-.374-.658h-3.031Z"/></svg>

Before

Width:  |  Height:  |  Size: 386 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="91.748 41.508 32 32"><path fill="none" d="M91.748 41.508h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M104.884 48.3h5.728c1.363 0 2.47.94 2.47 2.098v11.448c0 1.158-1.107 2.098-2.47 2.098h-5.728c-1.363 0-2.469-.94-2.469-2.098V50.398c0-1.158 1.106-2.098 2.469-2.098Zm-.004-2.692h5.729a5.162 5.162 0 0 1 5.164 5.164v13.472a5.164 5.164 0 0 1-1.514 3.649 5.143 5.143 0 0 1-3.65 1.515h-5.729a5.164 5.164 0 0 1-5.157-5.164V50.772c0-1.374.538-2.687 1.508-3.656a5.182 5.182 0 0 1 3.649-1.508Zm1.407 21.076a1.462 1.462 0 0 1 2.922 0 1.461 1.461 0 0 1-2.922 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

View File

@@ -1 +0,0 @@
<svg width="553" height="553"><path fill="#fff" d="M77 179a33 33 0 0 0-25 10 33 33 0 0 0-9 24v143a33 33 0 0 0 10 24 33 33 0 0 0 24 10c9 0 17-3 24-10a33 33 0 0 0 10-24V213c0-9-4-17-10-24a33 33 0 0 0-24-10zM352 51l24-44c1-3 1-5-2-6-3-2-5-1-7 2l-24 43a163 163 0 0 0-133 0L186 3c-2-3-4-4-7-2-2 1-3 3-1 6l23 44c-24 12-43 29-57 51a129 129 0 0 0-21 72h307c0-26-7-50-21-72a146 146 0 0 0-57-51zm-136 63a13 13 0 0 1-10 4 13 13 0 0 1-12-13c0-4 1-7 3-9 3-3 6-4 9-4s7 1 10 4c2 2 3 5 3 9s-1 7-3 9zm140 0a12 12 0 0 1-9 4c-4 0-7-1-9-4a12 12 0 0 1-4-9c0-4 1-7 4-9 2-3 5-4 9-4a12 12 0 0 1 9 4c2 2 3 5 3 9s-1 7-3 9zM124 407c0 10 4 19 11 26s15 10 26 10h24v76c0 9 4 17 10 24s15 10 24 10c10 0 18-3 25-10s10-15 10-24v-76h45v76c0 9 4 17 10 24s15 10 25 10c9 0 17-3 24-10s10-15 10-24v-76h25a35 35 0 0 0 25-10c7-7 11-16 11-26V185H124v222zm352-228a33 33 0 0 0-24 10 33 33 0 0 0-10 24v143a34 34 0 0 0 34 34c10 0 18-3 25-10s10-15 10-24V213c0-9-4-17-10-24a33 33 0 0 0-25-10z"/></svg>

Before

Width:  |  Height:  |  Size: 952 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="305.118 110.478 32 32"><path fill="none" d="M305.118 110.478h32v32h-32v-32Z"/><path d="m322.26 126.052-5.963-5.962a1.48 1.48 0 1 1 2.093-2.093l7.63 7.63c.47.47.47 1.233 0 1.703l-7.63 7.629a1.48 1.48 0 0 1-2.093 0 1.482 1.482 0 0 1 0-2.093l5.963-5.962a.603.603 0 0 0 0-.852Z"/></svg>

Before

Width:  |  Height:  |  Size: 380 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><circle cx="512" cy="512" r="512" fill="#999"/><path fill="#fff" d="M407.2 722.1c-10.1-6.7-19-15-26.5-24.5-8.2-9.9-15.7-20.3-22.7-31-16.3-23.9-29.1-50-38-77.5-10.7-32-15.8-62.7-15.8-92.7 0-33.5 7.2-62.7 21.4-87.2 10.4-19.2 26-35.2 44.8-46.5 18.1-11.3 39.2-17.5 60.6-17.9 7.5 0 15.6 1.1 24.1 3.2 6.2 1.7 13.6 4.5 22.8 7.9 11.7 4.5 18.1 7.2 20.3 7.9 6.8 2.6 12.6 3.6 17.1 3.6 3.4 0 8.3-1.1 13.8-2.8 3.1-1.1 9-3 17.3-6.6 8.2-3 14.8-5.5 19.9-7.5 7.9-2.3 15.5-4.5 22.4-5.5 8.3-1.3 16.6-1.7 24.5-1.1 15.1 1.1 29 4.3 41.4 9 21.7 8.7 39.3 22.4 52.4 41.8-5.5 3.4-10.7 7.4-15.5 11.7-10.4 9.2-19.2 20-26.2 32.1-9.2 16.4-13.9 35-13.7 53.7.3 23.1 6.2 43.4 17.9 61 8.3 12.8 19.3 23.8 32.7 32.7 6.6 4.5 12.4 7.6 17.9 9.6-2.6 8-5.4 15.8-8.6 23.5-7.4 17.2-16.2 33.7-26.7 49.3-9.2 13.4-16.5 23.5-22 30.1-8.6 10.2-16.8 17.9-25.2 23.4-9.2 6.1-19.9 9.3-31 9.3-7.5.3-14.9-.6-22-2.7-6.2-2-12.3-4.3-18.3-6.9-6.2-2.9-12.7-5.3-19.3-7.2-8.1-2.1-16.4-3.2-24.8-3.1-8.5 0-16.8 1.1-24.7 3.1-6.6 1.9-13 4.2-19.3 6.9-9 3.7-14.8 6.2-18.2 7.2-6.9 2-14 3.3-21.1 3.7-11.1 0-21.4-3.2-31.7-9.6zm146.1-393.6c-14.5 7.2-28.3 10.3-42.1 9.3-2.1-13.8 0-27.9 5.8-43.4 5.1-13.2 11.9-25.2 21.3-35.8 9.8-11.1 21.5-20.3 34.8-26.9 14.1-7.2 27.5-11.1 40.3-11.7 1.7 14.5 0 28.8-5.3 44.1-4.9 13.6-12.1 26.2-21.3 37.5-9.3 11.1-20.8 20.3-33.8 26.9z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg width="32" height="32"><path d="M29.307 9.932 26.161 0H5.796L2.692 9.932c-1.802 5.75.042 12.271 5.089 16.021L16.01 32l8.208-6.068c5.005-3.75 6.911-10.25 5.089-16.021l-8.214 6.104 3.12 9.938-8.208-6.13-8.208 6.104 3.141-9.911-8.25-6.063 10.177-.063 3.146-9.891 3.141 9.87z"/></svg>

Before

Width:  |  Height:  |  Size: 285 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="199"><path fill="#0089d6" d="M118.432 187.698c32.89-5.81 60.055-10.618 60.367-10.684l.568-.12-31.052-36.935c-17.078-20.314-31.051-37.014-31.051-37.11 0-.182 32.063-88.477 32.243-88.792.06-.105 21.88 37.567 52.893 91.32 29.035 50.323 52.973 91.815 53.195 92.203l.405.707-98.684-.012-98.684-.013 59.8-10.564zM0 176.435c0-.052 14.631-25.451 32.514-56.442l32.514-56.347 37.891-31.799C123.76 14.358 140.867.027 140.935.001c.069-.026-.205.664-.609 1.534s-18.919 40.582-41.145 88.25l-40.41 86.67-29.386.037c-16.162.02-29.385-.005-29.385-.057z"/></svg>

Before

Width:  |  Height:  |  Size: 604 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"><path d="M142.554 52.81c0-4.113 1.078-6.374 5.369-11.26 17.207-19.593 57.193-19.593 74.4 0 4.291 4.886 5.37 7.147 5.37 11.26v5.145h-85.14zm71.239-42.863 6.676-6.692 10.462 10.74 25.49-25.453 6.133 6.543-31.536 32.356-17.225-17.494Zm-34.474 3.377c-15.027-5.337-19.348-22.264-8.57-33.575 10.85-11.387 29.85-6.099 34.149 9.503 2.523 9.161-4.38 21.951-12.951 23.995-4.39 1.58-8.73 1.433-12.628.077z" style="fill:#024eff;fill-opacity:1;stroke-width:.999998" transform="translate(-142.554 44.365)"/></svg>

Before

Width:  |  Height:  |  Size: 564 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><rect width="512" height="512" fill="#1877f2" rx="76.8"/><path fill="#fff" d="m355.6 330 11.4-74h-71v-48c0-20.2 9.9-40 41.7-40H370v-63s-29.3-5-57.3-5c-58.5 0-96.7 35.4-96.7 99.6V256h-65v74h65v182h80V330z"/></svg>

Before

Width:  |  Height:  |  Size: 274 B

View File

@@ -1 +0,0 @@
<svg viewBox="0 0 24 24"><path fill="#231f20" d="M12 1A10.89 10.89 0 0 0 1 11.77 10.79 10.79 0 0 0 8.52 22c.55.1.75-.23.75-.52v-1.83c-3.06.65-3.71-1.44-3.71-1.44a2.86 2.86 0 0 0-1.22-1.58c-1-.66.08-.65.08-.65a2.31 2.31 0 0 1 1.68 1.11 2.37 2.37 0 0 0 3.2.89 2.33 2.33 0 0 1 .7-1.44c-2.44-.27-5-1.19-5-5.32a4.15 4.15 0 0 1 1.11-2.91 3.78 3.78 0 0 1 .11-2.84s.93-.29 3 1.1a10.68 10.68 0 0 1 5.5 0c2.1-1.39 3-1.1 3-1.1a3.78 3.78 0 0 1 .11 2.84A4.15 4.15 0 0 1 19 11.2c0 4.14-2.58 5.05-5 5.32a2.5 2.5 0 0 1 .75 2v2.95s.2.63.75.52A10.8 10.8 0 0 0 23 11.77 10.89 10.89 0 0 0 12 1"/></svg>

Before

Width:  |  Height:  |  Size: 582 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="236" preserveAspectRatio="xMidYMid"><path fill="#e24329" d="m128.075 236.075 47.104-144.97H80.97z"/><path fill="#fc6d26" d="M128.075 236.074 80.97 91.104H14.956z"/><path fill="#fca326" d="M14.956 91.104.642 135.16a9.752 9.752 0 0 0 3.542 10.903l123.891 90.012z"/><path fill="#e24329" d="M14.956 91.105H80.97L52.601 3.79c-1.46-4.493-7.816-4.492-9.275 0z"/><path fill="#fc6d26" d="m128.075 236.074 47.104-144.97h66.015z"/><path fill="#fca326" d="m241.194 91.104 14.314 44.056a9.752 9.752 0 0 1-3.543 10.903l-123.89 90.012z"/><path fill="#e24329" d="M241.194 91.105h-66.015l28.37-87.315c1.46-4.493 7.816-4.492 9.275 0z"/></svg>

Before

Width:  |  Height:  |  Size: 684 B

View File

@@ -1 +0,0 @@
<svg width="48" height="48"><path fill="#ffc107" d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"/><path fill="#ff3d00" d="m6.306 14.691 6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z"/><path fill="#4caf50" d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238A11.91 11.91 0 0 1 24 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z"/><path fill="#1976d2" d="M43.611 20.083H42V20H24v8h11.303a12.04 12.04 0 0 1-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z"/></svg>

Before

Width:  |  Height:  |  Size: 846 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="293.333" height="98.667" version="1.0" viewBox="0 0 220 74"><path fill="#fff" d="M62.7.6c-.4.4-.7 16.5-.7 35.9 0 26 .3 35.4 1.2 36.3 1.3 1.3 8.5 1.6 11.2.6 1.3-.5 1.6-2.4 1.6-10 0-13.3.7-13.3 13.8-.1L100.3 74h6.7c11.5 0 11.3-1-2.8-15.4-6.7-6.7-12.2-13-12.2-13.9 0-1 3.7-5.8 8.3-10.8 13.3-14.7 13.3-14.9 2.4-14.9h-7.2l-7.9 8.6c-4.8 5.2-8.5 8.4-9.5 8.2-1.4-.3-1.7-2.7-2.1-17.8L75.5.5l-6-.3c-3.4-.2-6.4 0-6.8.4zm55.7.7c-.3.8-.4 12.7-.2 26.4l.3 25 3 5.8c5 9.8 16.3 16.5 26 15.3 2.8-.3 3-.6 3.3-4.7.5-7.5-.3-9.1-4.6-9.1-5 0-10.2-2.9-12.4-7.1-2.1-4-2.6-17.7-.6-19.7.7-.7 4.2-1.2 8.5-1.2h7.3V19h-7.2c-4 0-7.8-.4-8.5-.8-.7-.5-1.3-4-1.5-9.2l-.3-8.5-6.3-.3c-4.7-.2-6.4.1-6.8 1.1zm-99.9 19C6.6 25.1.6 33.6.6 46c-.1 6.2.4 8.4 2.6 12.5 5.2 9.9 13.4 14.9 24.3 14.8 8.2 0 12.8-1.8 18.6-7 5.4-4.9 8.1-10.4 8.7-17.9.8-11-4.1-20.5-13.5-26.1-3.7-2.1-6.1-2.7-12.2-3-4.5-.1-8.9.2-10.6 1zm15.2 13.6c7 3.2 9.6 11.7 5.9 18.9-3.4 6.6-11.2 8.9-18.3 5.5-10-4.7-9.5-19.4.7-24.5 4.4-2.3 6.5-2.2 11.7.1zm137.6-13.2c-5.1 1.8-12.2 8.3-14.7 13.7-4.5 9.6-2.7 21.8 4.5 29.9 5.6 6.4 11.7 9.1 20.4 9.1 5.9 0 7.9-.4 12.8-3.1 3.1-1.7 5.7-2.7 5.7-2.3 0 .5 1.5 1.8 3.4 2.9 4.2 2.7 11.6 3.7 14.5 2.2 1.9-1.1 2.2-1.9 1.9-6.9-.3-5.5-.4-5.6-3.8-6.5-6.3-1.6-6.4-1.9-7-22l-.5-18.2-6.3-.3c-4.6-.2-6.4.1-6.8 1.1-.4 1.2-1.1 1.2-4.2.1-4.9-1.7-14.7-1.6-19.9.3zm15 12.7c5.4 2.2 8.7 7 8.7 12.4 0 10.9-9.3 17-19.5 12.8-7.5-3.2-10.1-14.4-4.8-20.7 2.7-3.2 7.4-5.8 10.6-5.9 1 0 3.2.6 5 1.4z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 560.459 32 32"><path fill="none" d="M587.811 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="m601.469 580.246-3.859 3.858a1.02 1.02 0 1 1-1.444-1.444l3.858-3.859 2.554-2.553 3.85-3.851a1.022 1.022 0 0 1 1.445 1.445l-3.851 3.85 2.299 2.299a1.23 1.23 0 0 0 1.717.014q3.089-2.988 6.218-.117.03.026.089.083c.34.34.527.8.527 1.276a1.78 1.78 0 0 1-.528 1.277l-1.366 1.366a5.557 5.557 0 0 1-7.861.004l-3.648-3.648Zm-.923-6.031-.267-.266a1.23 1.23 0 0 1-.014-1.717q2.988-3.09.117-6.218-.026-.031-.083-.089c-.341-.34-.8-.527-1.276-.527a1.78 1.78 0 0 0-1.277.528l-1.366 1.366a5.555 5.555 0 0 0-.004 7.861l1.616 1.616a.832.832 0 0 0 1.175 0l1.379-1.378a.833.833 0 0 0 0-1.176Z"/></svg>

Before

Width:  |  Height:  |  Size: 786 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 592.459 32 32"><path fill="none" d="M587.811 592.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M595.385 608.153a5.553 5.553 0 0 1 .004-7.852l1.365-1.365c.34-.34.795-.531 1.275-.527.476 0 .935.187 1.275.526l.083.089q2.868 3.125-.117 6.212a1.226 1.226 0 0 0 .014 1.714l6.036 6.036a1.226 1.226 0 0 0 1.714.014q3.086-2.985 6.212-.117l.089.083c.339.34.526.799.526 1.275.004.48-.187.935-.527 1.275l-1.365 1.365a5.553 5.553 0 0 1-7.852.004l-8.732-8.732Zm7.426-1.824a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Z"/></svg>

Before

Width:  |  Height:  |  Size: 746 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 521.772 32 32"><path fill="none" d="M587.811 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M601.822 543.465h7.624a3.94 3.94 0 0 0 3.942-3.938v-7.213a3.944 3.944 0 0 0-3.942-3.942h-11.27a3.944 3.944 0 0 0-3.942 3.942v7.213a3.944 3.944 0 0 0 2.901 3.798v2.543c0 1.413.787 1.726 1.757.699l2.93-3.102Zm-1.593-7.668a2.203 2.203 0 0 1 .001-3.112l.541-.541a.714.714 0 0 1 1.011 0l.033.035q1.136 1.239-.047 2.462a.486.486 0 0 0 .006.679l2.392 2.392c.186.186.49.189.679.006q1.224-1.183 2.462-.046l.035.033a.71.71 0 0 1 0 1.01l-.541.541a2.199 2.199 0 0 1-3.112.002l-3.46-3.461Z"/></svg>

Before

Width:  |  Height:  |  Size: 690 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#fff"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.644-1.142l-4.29 1.117a.85.85 0 0 1-1.037-1.036l1.116-4.289A9.959 9.959 0 0 1 2 12C2 6.477 6.477 2 12 2Zm1.252 11H8.75l-.102.007a.75.75 0 0 0 0 1.486l.102.007h4.502l.101-.007a.75.75 0 0 0 0-1.486L13.252 13Zm1.998-3.5h-6.5l-.102.007a.75.75 0 0 0 0 1.486L8.75 11h6.5l.102-.007a.75.75 0 0 0 0-1.486L15.25 9.5Z"/></svg>

Before

Width:  |  Height:  |  Size: 462 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1696255389449" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1922" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M435.2 704c-9 0-17.8-3.8-23.8-10.6l-115.2-128c-11.8-13.2-10.8-33.4 2.4-45.2 13.2-11.8 33.4-10.8 45.2 2.4l90.6 100.6 245.2-291.8c11.4-13.6 31.6-15.2 45-4 13.6 11.4 15.2 31.6 4 45l-268.8 320c-6 7-14.6 11.2-24 11.4-0.2 0.2-0.4 0.2-0.6 0.2z" p-id="1923"></path><path d="M800 928H224c-70.6 0-128-57.4-128-128V224c0-70.6 57.4-128 128-128h576c70.6 0 128 57.4 128 128v576c0 70.6-57.4 128-128 128zM224 160c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h576c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64H224z" p-id="1924"></path></svg>

Before

Width:  |  Height:  |  Size: 856 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1696245886035" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4133" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 132.717714c-9.435429 0-18.852571 3.84-29.147429 12.434286L194.011429 379.574857c-7.277714 6.418286-11.556571 15.433143-11.556572 28.288 0 22.272 16.713143 39.003429 39.424 39.003429 8.996571 0 18.432-3.437714 28.288-11.154286L512 222.281143l261.851429 213.430857c9.874286 7.716571 19.291429 11.154286 28.708571 11.154286 22.308571 0 39.003429-16.731429 39.003429-39.003429 0-12.854857-4.278857-21.869714-11.556572-28.288L541.147429 144.713143c-10.294857-8.137143-19.291429-11.995429-29.147429-11.995429z m0 758.564572c9.856 0 18.852571-3.84 29.147429-11.995429L829.988571 644.425143c7.277714-6.418286 11.556571-15.433143 11.556572-28.288 0-22.272-16.713143-39.424-38.985143-39.424-9.435429 0-18.870857 3.858286-28.708571 11.574857L512 801.718857 250.148571 588.288c-9.874286-7.716571-19.291429-11.574857-28.288-11.574857-22.710857 0-39.424 17.152-39.424 39.424 0 12.854857 4.278857 21.869714 11.556572 28.288l288.859428 234.422857c10.294857 8.594286 19.712 12.434286 29.147429 12.434286z" p-id="4134"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="679.559 521.772 32 32"><path fill="none" d="M679.559 521.772h32v32h-32v-32Z"/><path d="m695.559 536.034-5.305-5.305a1.229 1.229 0 1 0-1.738 1.738l5.305 5.305-5.305 5.305a1.229 1.229 0 0 0 0 1.738 1.23 1.23 0 0 0 1.738 0l5.305-5.305 5.305 5.305a1.229 1.229 0 1 0 1.738-1.738l-5.305-5.305 5.305-5.305a1.229 1.229 0 1 0-1.738-1.738l-5.305 5.305Z"/></svg>

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="450.189 521.772 32 32"><path fill="none" d="M450.189 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M470.477 545.236h2.795a5.13 5.13 0 0 0 3.639-1.515 5.126 5.126 0 0 0 1.515-3.639v-9.07a5.126 5.126 0 0 0-1.515-3.639 5.13 5.13 0 0 0-3.639-1.515h-14.166a5.13 5.13 0 0 0-3.639 1.515 5.127 5.127 0 0 0-1.516 3.639v9.07c0 1.363.544 2.675 1.516 3.639a5.13 5.13 0 0 0 3.639 1.515h2.795v1.234a3.217 3.217 0 0 0 3.216 3.216h2.144a3.217 3.217 0 0 0 3.216-3.216v-1.234Zm-11.371-15.753h14.166c.406 0 .79.166 1.08.449.283.29.449.674.449 1.08v9.07c0 .406-.166.79-.449 1.08-.29.283-.674.449-1.08.449h-14.166c-.406 0-.79-.166-1.08-.449a1.55 1.55 0 0 1-.45-1.08v-9.07c0-.406.167-.79.45-1.08.29-.283.674-.449 1.08-.449Z"/></svg>

Before

Width:  |  Height:  |  Size: 816 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="105.516 106.603 32 32"><path fill="none" d="M105.516 106.603h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M118.954 115.165a2.563 2.563 0 0 1 2.562-2.562 2.563 2.563 0 0 1 0 5.124 2.563 2.563 0 0 1-2.562-2.562Zm0 7.438a2.563 2.563 0 0 1 2.562-2.562 2.563 2.563 0 0 1 0 5.124 2.563 2.563 0 0 1-2.562-2.562Zm0 7.438a2.563 2.563 0 0 1 2.562-2.562 2.563 2.563 0 0 1 0 5.124 2.563 2.563 0 0 1-2.562-2.562Z"/></svg>

Before

Width:  |  Height:  |  Size: 507 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="187.624 70.761 32 32"><path fill="none" d="M187.624 70.761h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M199.771 89.326h7.706a.847.847 0 0 1 0 1.693h-7.706a.847.847 0 0 1 0-1.693Zm0-3.412h7.706a.848.848 0 0 1 0 1.694h-7.706a.848.848 0 0 1 0-1.694Zm-2.836-4.797v11.289a2.845 2.845 0 0 0 2.845 2.842h7.688a2.845 2.845 0 0 0 2.845-2.842V81.117a2.846 2.846 0 0 0-2.845-2.843h-7.688a2.846 2.846 0 0 0-2.845 2.843Zm2.836 1.386h7.706a.847.847 0 0 1 0 1.693h-7.706a.847.847 0 0 1 0-1.693Z"/></svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694049173782" class="icon" viewBox="0 0 1024 1024" width="24" height="24" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="992" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M891.64 184.73H620.41c-27.41 0-54.41-7.77-77.32-22.5L428.13 87.36C402.77 71 372.91 62 342.64 62H131.95C93.5 62 62 93.5 62 132.36v759.68C62 930.91 93.5 962 131.95 962h759.68c38.86 0 70.36-31.09 70.36-69.96V255.09c0.01-38.86-31.49-70.36-70.35-70.36zM480.5 753.77c0 16.77-13.5 30.68-30.68 30.68-16.77 0-30.68-13.91-30.68-30.68V523.04l-31.91 55.64c-8.59 14.32-27.41 19.64-42.14 11.04-14.32-8.59-19.64-27.41-11.05-41.73l89.18-154.64c6.96-12.27 21.27-18 34.77-14.32 13.09 3.27 22.5 15.55 22.5 29.45v345.29z m209.04-139.5l-89.18 154.64c-5.32 9.82-15.55 15.55-26.59 15.55-2.46 0-5.32-0.41-7.77-1.23-13.5-3.68-22.91-15.55-22.91-29.46V408.5c0-16.77 13.91-30.68 30.68-30.68 17.18 0 30.68 13.91 30.68 30.68v230.73l31.91-55.64c8.59-14.73 27.41-19.64 42.14-11.05 14.73 8.6 19.64 27.01 11.04 41.73z" p-id="993"></path></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="129.858 75.669 32 32"><path fill="none" d="M129.858 75.669h32v32h-32v-32Z"/><path d="M138.617 99.535H153.1a2.76 2.76 0 0 0 2.758-2.758V89.32a2.76 2.76 0 0 0-2.758-2.759h-5.397a.552.552 0 0 1-.552-.552v-.827a1.38 1.38 0 0 0-1.379-1.38h-5.776c-.761 0-1.816.437-2.355.976l-.808.808c-.538.538-.975 1.593-.975 2.354v8.837a2.76 2.76 0 0 0 2.759 2.758Z"/></svg>

Before

Width:  |  Height:  |  Size: 452 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="59.535 106.603 32 32"><path fill="none" d="M59.535 106.603h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M68.294 130.469h14.482a2.76 2.76 0 0 0 2.759-2.758v-7.457a2.76 2.76 0 0 0-2.759-2.759H77.38a.552.552 0 0 1-.552-.551v-.828a1.38 1.38 0 0 0-1.379-1.379h-5.776c-.761 0-1.816.437-2.355.975l-.808.808c-.538.538-.975 1.593-.975 2.355v8.836a2.76 2.76 0 0 0 2.759 2.758Zm11.948-5.308h1.682a.794.794 0 1 0 0-1.586h-1.682v-1.683a.794.794 0 1 0-1.587 0v1.683h-1.683a.794.794 0 1 0 0 1.586h1.683v1.683a.795.795 0 0 0 1.587 0v-1.683Z"/></svg>

Before

Width:  |  Height:  |  Size: 632 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="404.315 521.772 32 32"><path fill-rule="evenodd" d="M411.985 533.763a1.815 1.815 0 0 1-1.812 1.812c-.479 0-.943-.196-1.284-.536a1.834 1.834 0 0 1-.529-1.276v-2.806c0-1.312.522-2.566 1.45-3.494a4.938 4.938 0 0 1 3.494-1.45h2.799c.486 0 .942.196 1.283.536.341.334.529.798.529 1.276a1.8 1.8 0 0 1-.529 1.283c-.341.341-.797.53-1.283.53h-2.799c-.348 0-.688.137-.935.391-.246.247-.384.58-.384.928v2.806Zm4.118 12.143a1.802 1.802 0 0 1 1.812 1.812c0 .479-.188.943-.529 1.276a1.81 1.81 0 0 1-1.283.537h-2.799a4.938 4.938 0 0 1-3.494-1.45 4.939 4.939 0 0 1-1.45-3.495v-2.805c0-.479.196-.935.529-1.276a1.824 1.824 0 0 1 1.284-.537c.485 0 .942.196 1.283.537.341.341.529.797.529 1.276v2.805c0 .348.138.682.384.928.247.254.587.392.935.392h2.799Zm8.424-16.268c-.486 0-.943-.189-1.283-.53a1.8 1.8 0 0 1-.529-1.283c0-.478.188-.942.529-1.276.34-.34.797-.536 1.283-.536h2.798a4.94 4.94 0 0 1 3.495 1.45 4.938 4.938 0 0 1 1.45 3.494v2.806a1.823 1.823 0 0 1-1.813 1.812c-.486 0-.942-.196-1.283-.536a1.8 1.8 0 0 1-.529-1.276v-2.806a1.31 1.31 0 0 0-.385-.928 1.302 1.302 0 0 0-.935-.391h-2.798Zm4.118 12.143c0-.479.188-.935.529-1.276a1.81 1.81 0 0 1 1.283-.537 1.82 1.82 0 0 1 1.813 1.813v2.805a4.939 4.939 0 0 1-1.45 3.495 4.94 4.94 0 0 1-3.495 1.45h-2.798c-.486 0-.943-.196-1.283-.537a1.784 1.784 0 0 1-.529-1.276 1.804 1.804 0 0 1 1.812-1.812h2.798c.348 0 .689-.138.935-.392a1.31 1.31 0 0 0 .385-.928v-2.805Z"/><path fill="none" d="M404.315 521.772h32v32h-32v-32Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="404.315 560.459 32 32"><path fill="none" d="M404.315 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M414.29 566.512c0-.478.189-.935.53-1.275.34-.341.797-.537 1.283-.537a1.824 1.824 0 0 1 1.812 1.812v2.806a4.938 4.938 0 0 1-1.45 3.494 4.938 4.938 0 0 1-3.494 1.45h-2.798c-.486 0-.943-.195-1.284-.536a1.792 1.792 0 0 1-.529-1.276 1.805 1.805 0 0 1 1.813-1.813h2.798c.348 0 .689-.137.935-.391.247-.246.384-.58.384-.928v-2.806Zm-4.117 15.768a1.804 1.804 0 0 1-1.813-1.812c0-.478.189-.942.529-1.276a1.811 1.811 0 0 1 1.284-.536h2.798c1.312 0 2.566.522 3.494 1.449a4.94 4.94 0 0 1 1.45 3.495v2.805a1.824 1.824 0 0 1-1.812 1.813c-.486 0-.943-.196-1.283-.537a1.797 1.797 0 0 1-.53-1.276V583.6c0-.348-.137-.682-.384-.928a1.303 1.303 0 0 0-.935-.392h-2.798Zm20.284-11.643c.486 0 .943.189 1.283.53.341.34.53.797.53 1.283 0 .478-.189.942-.53 1.276-.34.341-.797.536-1.283.536h-2.798a4.94 4.94 0 0 1-3.495-1.45 4.937 4.937 0 0 1-1.449-3.494v-2.806a1.82 1.82 0 0 1 1.812-1.812c.486 0 .942.196 1.283.537.341.34.529.797.529 1.275v2.806c0 .348.138.682.385.928.246.254.587.391.935.391h2.798Zm-4.118 15.768c0 .479-.188.936-.529 1.276a1.81 1.81 0 0 1-1.283.537 1.82 1.82 0 0 1-1.812-1.813V583.6a4.944 4.944 0 0 1 4.944-4.944h2.798c.486 0 .943.195 1.283.536.341.334.53.798.53 1.276 0 .486-.189.942-.53 1.283a1.8 1.8 0 0 1-1.283.529h-2.798c-.348 0-.689.138-.935.392a1.31 1.31 0 0 0-.385.928v2.805Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="13.554 106.603 32 32"><path fill="none" d="M13.554 106.603h32v32h-32v-32Z"/><path d="M32.944 132.507h3.661a1 1 0 0 0 1-1v-7.837h2.335c.552 0 .683-.316.293-.707l-9.972-9.971a.999.999 0 0 0-1.414 0l-9.971 9.971c-.391.391-.259.707.293.707h2.334v7.837a1 1 0 0 0 1 1h3.661a1 1 0 0 0 1-1v-2.966a.5.5 0 0 1 .5-.5h3.78a.5.5 0 0 1 .5.5v2.966c0 .552.449 1 1 1Z"/></svg>

Before

Width:  |  Height:  |  Size: 457 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" viewBox="66.993 897.484 26 26"><linearGradient id="a" x1=".148" x2=".845" y1=".851" y2=".154" gradientTransform="matrix(26.301 0 0 26.331 90.674 911.757)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0071ff"/><stop offset="1" stop-color="#00bfe1"/></linearGradient><defs><linearGradient xlink:href="#a" id="b" x1=".148" x2=".845" y1=".851" y2=".154" gradientTransform="matrix(26.00048 0 0 25.99935 66.993 897.485)" gradientUnits="userSpaceOnUse"/></defs><path fill="url(#b)" d="m89.318 903.552-2.135 2.122c-.376.337-.558.879-.347 1.337 1.422 2.976.882 6.524-1.452 8.856-2.335 2.331-5.887 2.87-8.866 1.449-.439-.197-.954-.03-1.292.312l-2.17 2.167a1.154 1.154 0 0 0 .208 1.81 13.005 13.005 0 0 0 15.91-1.912 12.97 12.97 0 0 0 1.956-15.887 1.154 1.154 0 0 0-1.812-.254zm-18.467-2.305a12.969 12.969 0 0 0-2.02 15.885 1.154 1.154 0 0 0 1.812.254l2.124-2.11c.385-.336.572-.884.359-1.348-1.423-2.976-.884-6.524 1.451-8.856 2.334-2.332 5.887-2.871 8.866-1.45.434.194.942.033 1.281-.3l2.182-2.18a1.152 1.152 0 0 0-.208-1.81 13.009 13.009 0 0 0-15.893 1.973z"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +0,0 @@
<svg viewBox="0 0 347.97 347.97"><path fill="none" stroke="red" stroke-width="14.827" d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z"/><g fill="red"><path d="m238.802 115.023-111.573 114.68-8.6-8.367L230.2 106.656z"/><path d="m125.559 108.093 114.68 111.572-8.368 8.601-114.68-111.572z"/></g></svg>

Before

Width:  |  Height:  |  Size: 440 B

View File

@@ -1 +0,0 @@
<svg viewBox="0 0 347.97 347.97"><path fill="none" stroke="red" stroke-width="14.827" d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z"/><path fill="red" d="m231.442 247.498-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z"/></svg>

Before

Width:  |  Height:  |  Size: 746 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="261" height="253" version="1.2"><path fill="#0ff" d="M1 217c0-5.5 4.5-10 10-10h60c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10H11c-5.5 0-10-4.5-10-10z"/><path fill="#487997" d="M89 216c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10H99c-5.5 0-10-4.5-10-10z"/><path fill="#c0ff00" d="M3 166c0-5.5 4.5-10 10-10h34c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10H13c-5.5 0-10-4.5-10-10z"/><path fill="#00f" d="M63 166c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10H73c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="M140 215c0-5.5 4.5-10 10-10h26c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-26c-5.5 0-10-4.5-10-10zm50 0c0-5.5 4.5-10 10-10h26c5.5 0 10 4.5 10 10v28c0 5.5-4.5 10-10 10h-26c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="M112 166c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10zm53-1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm51-1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="M0 115c0-5.5 4.5-10 10-10h61c5.5 0 10 4.5 10 10v23c0 5.5-4.5 10-10 10H10c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="M90 116c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10zm49 0c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10zm52-1c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-23c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="M2 62c0-5.5 4.5-10 10-10h50c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H12C6.5 98 2 93.5 2 88z"/><path fill="#00ffa8" d="M79 62c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H89c-5.5 0-10-4.5-10-10zm52 2c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm51-1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zM0 10C0 4.5 4.5 0 10 0h28c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10H10C4.5 47 0 42.5 0 37zm54 1c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H64c-5.5 0-10-4.5-10-10zm51 1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm51 1c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path d="M16.7 171.9v1.9q-1.1-.5-2.1-.8-1-.2-1.9-.2-1.7 0-2.5.6-.9.6-.9 1.8 0 .9.6 1.4.6.5 2.2.8l1.2.3q2.2.4 3.2 1.4 1.1 1.1 1.1 2.9 0 2.1-1.4 3.2-1.5 1.1-4.2 1.1-1 0-2.2-.3-1.2-.2-2.4-.6v-2.1q1.2.7 2.3 1 1.2.4 2.3.4 1.7 0 2.6-.7.9-.6.9-1.9 0-1.1-.6-1.7-.7-.6-2.2-.9l-1.2-.2q-2.2-.4-3.2-1.4-1-.9-1-2.6 0-1.9 1.4-3 1.3-1.1 3.7-1.1 1.1 0 2.1.1 1.1.2 2.2.6zm13 7.5v6.6h-1.8v-6.5q0-1.6-.6-2.4-.6-.7-1.8-.7-1.5 0-2.3.9-.9.9-.9 2.5v6.2h-1.8v-15.2h1.8v6q.7-1 1.5-1.5.9-.5 2.1-.5 1.8 0 2.8 1.2 1 1.1 1 3.4zm3.6 6.6v-10.9h1.8V186zm0-12.9v-2.3H35v2.3zm9.4-2.3h1.7v1.5h-1.7q-.9 0-1.3.4t-.4 1.4v1h3v1.4h-3v9.5h-1.8v-9.5h-1.7v-1.4h1.7v-.8q0-1.8.8-2.7.9-.8 2.7-.8zm2.9 1.2h1.8v3.1h3.7v1.4h-3.7v5.9q0 1.3.3 1.7.4.4 1.5.4h1.9v1.5h-1.9q-2.1 0-2.8-.8-.8-.8-.8-2.8v-5.9h-1.4v-1.4h1.4z"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="260" height="253" version="1.2"><path fill="#c0ff00" d="M0 167c0-5.5 4.5-10 10-10h90c5.5 0 10 4.5 10 10v23c0 5.5-4.5 10-10 10H10c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="M3 63c0-5.5 4.5-10 10-10h46c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10H13C7.5 97 3 92.5 3 87zm-2 51c0-5.5 4.5-10 10-10h62c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H11c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="M114 166c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10zm-24-49c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v22c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm-9-53c0-5.5 4.5-10 10-10h22c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H91c-5.5 0-10-4.5-10-10zM54 10c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10H64c-5.5 0-10-4.5-10-10zM2 10C2 4.5 6.5 0 12 0h26c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10H12C6.5 47 2 42.5 2 37z"/><path fill="#0ff" d="M2 216c0-5.5 4.5-10 10-10h59c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H12c-5.5 0-10-4.5-10-10z"/><path fill="#487997" d="M89 217c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10H99c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="M141 217c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10zm50-1c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-23c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="M166 164c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10zm49 1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm-77-54c0-5.5 4.5-10 10-10h28c5.5 0 10 4.5 10 10v29c0 5.5-4.5 10-10 10h-28c-5.5 0-10-4.5-10-10zm54 1c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v29c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm-63-48c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10zm53-2c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10zm-77-51c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10zm49 1c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path d="M42.7 172.9v1.9q-1.1-.5-2.1-.8-1-.2-1.9-.2-1.7 0-2.5.6-.9.6-.9 1.8 0 .9.6 1.4.6.5 2.2.8l1.2.3q2.2.4 3.2 1.4 1.1 1.1 1.1 2.9 0 2.1-1.4 3.2-1.5 1.1-4.2 1.1-1 0-2.2-.3-1.2-.2-2.4-.6v-2.1q1.2.7 2.3 1 1.2.4 2.3.4 1.7 0 2.6-.7.9-.6.9-1.9 0-1.1-.6-1.7-.7-.6-2.2-.9l-1.2-.2q-2.2-.4-3.2-1.4-1-.9-1-2.6 0-1.9 1.4-3 1.3-1.1 3.7-1.1 1.1 0 2.1.1 1.1.2 2.2.6zm13 7.5v6.6h-1.8v-6.5q0-1.6-.6-2.4-.6-.7-1.8-.7-1.5 0-2.3.9-.9.9-.9 2.5v6.2h-1.8v-15.2h1.8v6q.7-1 1.5-1.5.9-.5 2.1-.5 1.8 0 2.8 1.2 1 1.1 1 3.4zm3.6 6.6v-10.9h1.8V187zm0-12.9v-2.3H61v2.3zm9.4-2.3h1.7v1.5h-1.7q-.9 0-1.3.4t-.4 1.4v1h3v1.4h-3v9.5h-1.8v-9.5h-1.7v-1.4h1.7v-.8q0-1.8.8-2.7.9-.8 2.7-.8zm2.9 1.2h1.8v3.1h3.7v1.4h-3.7v5.9q0 1.3.3 1.7.4.4 1.5.4h1.9v1.5h-1.9q-2.1 0-2.8-.8-.8-.8-.8-2.8v-5.9h-1.4v-1.4h1.4z"/></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

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