mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-23 05:08:13 -05:00
Compare commits
No commits in common. "d4ea530bfc073c5e6f25820140c438c9e3ba4766" and "a5dbc5693d24373805879d69b873c9df4de59598" have entirely different histories.
d4ea530bfc
...
a5dbc5693d
114
README.md
114
README.md
@ -43,63 +43,63 @@ add your theme to [service/singleton/frontend-templates.yaml](service/singleton/
|
|||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba"><img src="https://private-avatars.githubusercontent.com/u/29243953?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzMjAsIm5iZiI6MTczNDYyMTEyMCwicGF0aCI6Ii91LzI5MjQzOTUzIn0.rwVx1tPtpcOWYkG18k8qE2a1wKAzEQt-_RnEb4laXKE&v=4" width="50;" alt="naiba"/></a>
|
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba"><img src="https://avatars.githubusercontent.com/u/29243953?v=4" width="50;" alt="naiba"/></a>
|
||||||
<a href="https://github.com/uubulb" title="UUBulb"><img src="https://private-avatars.githubusercontent.com/u/35923940?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIyMDAsIm5iZiI6MTczNDYyMTAwMCwicGF0aCI6Ii91LzM1OTIzOTQwIn0.EOWIZ7OrftgKbImlMrQ8h0fvrFWMXY2wqos7DLHO1eI&v=4" width="50;" alt="UUBulb"/></a>
|
<a href="https://github.com/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></a>
|
||||||
<a href="https://github.com/AkkiaS7" title="Akkia"><img src="https://private-avatars.githubusercontent.com/u/68485070?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzY4NDg1MDcwIn0.RQnNWN6jT4XdlXZRzJRzzWFY21SG4LGDrxhPHS0TFAo&v=4" width="50;" alt="Akkia"/></a>
|
<a href="https://github.com/AkkiaS7" title="Akkia"><img src="https://avatars.githubusercontent.com/u/68485070?v=4" width="50;" alt="Akkia"/></a>
|
||||||
<a href="https://github.com/Erope" title="卖女孩的小火柴"><img src="https://private-avatars.githubusercontent.com/u/44471469?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI3NDAsIm5iZiI6MTczNDYyMTU0MCwicGF0aCI6Ii91LzQ0NDcxNDY5In0.S5vptLqOsPD4jRkjw7Yn0ToKbYK3mAPMo90X2EzRfeE&v=4" width="50;" alt="卖女孩的小火柴"/></a>
|
<a href="https://github.com/Erope" title="卖女孩的小火柴"><img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/></a>
|
||||||
<a href="https://github.com/nap0o" title="nap0o"><img src="https://private-avatars.githubusercontent.com/u/144927971?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIyNjAsIm5iZiI6MTczNDYyMTA2MCwicGF0aCI6Ii91LzE0NDkyNzk3MSJ9.YSvr582O1qaaojxKou0-YVDd9oGSdIGBjK3Ogu-tbv0&v=4" width="50;" alt="nap0o"/></a>
|
<a href="https://github.com/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
|
||||||
<a href="https://github.com/dysf888" title="黑歌"><img src="https://private-avatars.githubusercontent.com/u/47450409?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI3NDAsIm5iZiI6MTczNDYyMTU0MCwicGF0aCI6Ii91LzQ3NDUwNDA5In0.X-94XiR9XjOtTfFhnv73SZbHZdrieh8rkNZgwwarmlQ&v=4" width="50;" alt="黑歌"/></a>
|
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
|
||||||
<a href="https://github.com/xykt" title="xykt"><img src="https://private-avatars.githubusercontent.com/u/152045469?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzE1MjA0NTQ2OSJ9.nlo8iL9hzbSvrG7goRu6uXmvcpue80mmFQxhJezRub4&v=4" width="50;" alt="xykt"/></a>
|
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
|
||||||
<a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://private-avatars.githubusercontent.com/u/22676744?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2ODAsIm5iZiI6MTczNDYyMTQ4MCwicGF0aCI6Ii91LzIyNjc2NzQ0In0.OXBx2geYyT5IGqK8mk2DPRiA5pG5gfh_DytHrRWlRf8&v=4" width="50;" alt="MikoyChinese"/></a>
|
<a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a>
|
||||||
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://private-avatars.githubusercontent.com/u/24974735?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzMjAsIm5iZiI6MTczNDYyMTEyMCwicGF0aCI6Ii91LzI0OTc0NzM1In0.Q8H6JQyp1k3hsPZQxqA9ShR793uzUk4CMD3PWmOhBlE&v=4" width="50;" alt="JackieSung4ev"/></a>
|
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
|
||||||
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://private-avatars.githubusercontent.com/u/116849421?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIyMDAsIm5iZiI6MTczNDYyMTAwMCwicGF0aCI6Ii91LzExNjg0OTQyMSJ9.bQMydR_ESUlDjJFCXmn_5O81h75PSl4kSXZouV0vMeo&v=4" width="50;" alt="Kris"/></a>
|
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
|
||||||
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://private-avatars.githubusercontent.com/u/18618627?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1MDAsIm5iZiI6MTczNDYyMTMwMCwicGF0aCI6Ii91LzE4NjE4NjI3In0.k_xHIfcRF44IxRrEGZy653w200Pch6GhRG1B-yU3YSo&v=4" width="50;" alt="Lemoe"/></a>
|
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
|
||||||
<a href="https://github.com/spiritLHLS" title="spiritlhl"><img src="https://private-avatars.githubusercontent.com/u/103393591?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI4NjAsIm5iZiI6MTczNDYyMTY2MCwicGF0aCI6Ii91LzEwMzM5MzU5MSJ9.gRmONJHaiPxRnQ4tJt6--3DEFmPTorByGuvotB7XiNs&v=4" width="50;" alt="spiritlhl"/></a>
|
<a href="https://github.com/spiritLHLS" title="spiritlhl"><img src="https://avatars.githubusercontent.com/u/103393591?v=4" width="50;" alt="spiritlhl"/></a>
|
||||||
<a href="https://github.com/liuyanxi975" title="刘颜溪"><img src="https://private-avatars.githubusercontent.com/u/24417037?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzI0NDE3MDM3In0.L6XbaOiuQfT4UC16dnH_cdl4XMeSOMp_5HNFfRZIYt0&v=4" width="50;" alt="刘颜溪"/></a>
|
<a href="https://github.com/liuyanxi975" title="刘颜溪"><img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/></a>
|
||||||
<a href="https://github.com/CosmosZ-code" title="CosmosZ-code"><img src="https://private-avatars.githubusercontent.com/u/81398224?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzgxMzk4MjI0In0.yMwP5x67nhcd94ldSVY0NltgXoXm_iIbIJsvHi1F-xI&v=4" width="50;" alt="CosmosZ-code"/></a>
|
<a href="https://github.com/CosmosZ-code" title="CosmosZ-code"><img src="https://avatars.githubusercontent.com/u/81398224?v=4" width="50;" alt="CosmosZ-code"/></a>
|
||||||
<a href="https://github.com/lvgj-stack" title="Ko no dio"><img src="https://private-avatars.githubusercontent.com/u/38449861?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI4NjAsIm5iZiI6MTczNDYyMTY2MCwicGF0aCI6Ii91LzM4NDQ5ODYxIn0.YR4PY5C3_e3hbmo8cChvQ7kzvGqSx0jsRTkFOCzGRg4&v=4" width="50;" alt="Ko no dio"/></a>
|
<a href="https://github.com/lvgj-stack" title="Ko no dio"><img src="https://avatars.githubusercontent.com/u/38449861?v=4" width="50;" alt="Ko no dio"/></a>
|
||||||
<a href="https://github.com/hhhkkk520" title="Kris"><img src="https://private-avatars.githubusercontent.com/u/52115472?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIwODAsIm5iZiI6MTczNDYyMDg4MCwicGF0aCI6Ii91LzUyMTE1NDcyIn0.DKZHWeSXBMLFuWT8mBGEZC1VHFcx46f3bxOAtKGiKIY&v=4" width="50;" alt="Kris"/></a>
|
<a href="https://github.com/hhhkkk520" title="Kris"><img src="https://avatars.githubusercontent.com/u/52115472?v=4" width="50;" alt="Kris"/></a>
|
||||||
<a href="https://github.com/1ridic" title="1ridic"><img src="https://private-avatars.githubusercontent.com/u/88495501?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzMjAsIm5iZiI6MTczNDYyMTEyMCwicGF0aCI6Ii91Lzg4NDk1NTAxIn0.Fq4BjwCvgsBLUCsI5tVM6B3yu7NGnNirwTVLPv6t8_M&v=4" width="50;" alt="1ridic"/></a>
|
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
||||||
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://private-avatars.githubusercontent.com/u/36563672?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzODAsIm5iZiI6MTczNDYyMTE4MCwicGF0aCI6Ii91LzM2NTYzNjcyIn0.T2eDVOJ3lDHabqdd9ndHXyM-Cug4LFkj3VjcZMUQeLo&v=4" width="50;" alt="Mmx"/></a>
|
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/></a>
|
||||||
<a href="https://github.com/rootmelo92118" title="rootmelo92118"><img src="https://private-avatars.githubusercontent.com/u/32770959?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzMyNzcwOTU5In0.JTG3Ku8M5HNY-MzNgefInvXNr6A_iHG0T-ngPaYv-Jk&v=4" width="50;" alt="rootmelo92118"/></a>
|
<a href="https://github.com/rootmelo92118" title="rootmelo92118"><img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/></a>
|
||||||
<a href="https://github.com/Moraxyc" title="Moraxyc"><img src="https://private-avatars.githubusercontent.com/u/69713071?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2MjAsIm5iZiI6MTczNDYyMTQyMCwicGF0aCI6Ii91LzY5NzEzMDcxIn0.u1Ntl70CQ1nKmo40_-pZSvgCI81c8Gcied93rK9eijs&v=4" width="50;" alt="Moraxyc"/></a>
|
<a href="https://github.com/Moraxyc" title="Moraxyc"><img src="https://avatars.githubusercontent.com/u/69713071?v=4" width="50;" alt="Moraxyc"/></a>
|
||||||
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://private-avatars.githubusercontent.com/u/47970938?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1NjAsIm5iZiI6MTczNDYyMTM2MCwicGF0aCI6Ii91LzQ3OTcwOTM4In0.KOf6Mgq4F3FaG0VrdcBOlhfVwMgKlF7Otspqb2ncMbA&v=4" width="50;" alt="zhucaidan"/></a>
|
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></a>
|
||||||
<a href="https://github.com/iilemon" title="Sean"><img src="https://private-avatars.githubusercontent.com/u/33201711?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzMzMjAxNzExIn0.Cuc-K4ncQBMd91SmSyQAkGrXZ6ZtTTMr07VzFGcPvA8&v=4" width="50;" alt="Sean"/></a>
|
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
||||||
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://private-avatars.githubusercontent.com/u/62703343?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI5MjAsIm5iZiI6MTczNDYyMTcyMCwicGF0aCI6Ii91LzYyNzAzMzQzIn0.AH9eRZHnh6k_Sa-UeXjQepexshDlnYMr5fHYS4tXod4&v=4" width="50;" alt="fscarmen"/></a>
|
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
||||||
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://private-avatars.githubusercontent.com/u/9103372?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzMjAsIm5iZiI6MTczNDYyMTEyMCwicGF0aCI6Ii91LzkxMDMzNzIifQ.8lBpXt9jm312dpg7fv6Vgj87gkBQP_3QakDfThlDIFs&v=4" width="50;" alt="no-name-now"/></a>
|
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
||||||
<a href="https://github.com/weblate" title="Weblate (bot)"><img src="https://private-avatars.githubusercontent.com/u/1607653?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzE2MDc2NTMifQ.vniNKuEpfzVSUHlk1Vt13aAoOpQ0URGrdPa8bJNR4JU&v=4" width="50;" alt="Weblate (bot)"/></a>
|
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
|
||||||
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://private-avatars.githubusercontent.com/u/79034142?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2MjAsIm5iZiI6MTczNDYyMTQyMCwicGF0aCI6Ii91Lzc5MDM0MTQyIn0.0X2TatnUWmmACBQ9Pa8i1hIabQLQh-j4NwcH8Pq5VZs&v=4" width="50;" alt="HsukqiLee"/></a>
|
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
||||||
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://private-avatars.githubusercontent.com/u/53445798?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzODAsIm5iZiI6MTczNDYyMTE4MCwicGF0aCI6Ii91LzUzNDQ1Nzk4In0._Z6A_8rWlQGxdmG2VVdVe82hVJYngKfJ3JFMCBbomrk&v=4" width="50;" alt="Darc Z."/></a>
|
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
||||||
<a href="https://github.com/Creling" title="Creling"><img src="https://private-avatars.githubusercontent.com/u/43109504?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzMjAsIm5iZiI6MTczNDYyMTEyMCwicGF0aCI6Ii91LzQzMTA5NTA0In0.w-y9swb5L2BfJDub3uU0nzreAxvOzohWatqnC_dXEdI&v=4" width="50;" alt="Creling"/></a>
|
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
||||||
<a href="https://github.com/coreff" title="Core F"><img src="https://private-avatars.githubusercontent.com/u/38347122?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIzODAsIm5iZiI6MTczNDYyMTE4MCwicGF0aCI6Ii91LzM4MzQ3MTIyIn0.TC3ZbQXaq6lyrUbRnyxf2uIgSHRqE8tGDxNs4W8-Rao&v=4" width="50;" alt="Core F"/></a>
|
<a href="https://github.com/weblate" title="Weblate (bot)"><img src="https://avatars.githubusercontent.com/u/1607653?v=4" width="50;" alt="Weblate (bot)"/></a>
|
||||||
<a href="https://github.com/adminsama" title="adminsama"><img src="https://private-avatars.githubusercontent.com/u/60880076?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2ODAsIm5iZiI6MTczNDYyMTQ4MCwicGF0aCI6Ii91LzYwODgwMDc2In0.DwpFVReVHItkQ4yvfCIHu3-UaSoUHP5QKle4bp2d1sU&v=4" width="50;" alt="adminsama"/></a>
|
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
||||||
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://private-avatars.githubusercontent.com/u/15900800?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1NjAsIm5iZiI6MTczNDYyMTM2MCwicGF0aCI6Ii91LzE1OTAwODAwIn0.nq-thpXO4q26saF_XbP0wdfqi2PzX3mK7VN0q6qrPlo&v=4" width="50;" alt="Acgpiano"/></a>
|
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
|
||||||
<a href="https://github.com/eya46" title="eya46"><img src="https://private-avatars.githubusercontent.com/u/61458340?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2MjAsIm5iZiI6MTczNDYyMTQyMCwicGF0aCI6Ii91LzYxNDU4MzQwIn0.qPtO4QrtwGjUFse4lXvfl1VqCRfC0IDo4JX0mvdTHPk&v=4" width="50;" alt="eya46"/></a>
|
<a href="https://github.com/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></a>
|
||||||
<a href="https://github.com/guoyongchang" title="guoyongchang"><img src="https://private-avatars.githubusercontent.com/u/10484506?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2ODAsIm5iZiI6MTczNDYyMTQ4MCwicGF0aCI6Ii91LzEwNDg0NTA2In0.D5B9KZICiGiFOuRA6NbC8fZXOE2T-LUGeMSynw_3ycg&v=4" width="50;" alt="guoyongchang"/></a>
|
<a href="https://github.com/guoyongchang" title="guoyongchang"><img src="https://avatars.githubusercontent.com/u/10484506?v=4" width="50;" alt="guoyongchang"/></a>
|
||||||
<a href="https://github.com/hiDandelion" title="hiDandelion"><img src="https://private-avatars.githubusercontent.com/u/77157418?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI3NDAsIm5iZiI6MTczNDYyMTU0MCwicGF0aCI6Ii91Lzc3MTU3NDE4In0.7HQMkSPQwUEGdif5sZO9vKIcGJYt-6k2g_z6qChh2L8&v=4" width="50;" alt="hiDandelion"/></a>
|
<a href="https://github.com/hiDandelion" title="hiDandelion"><img src="https://avatars.githubusercontent.com/u/77157418?v=4" width="50;" alt="hiDandelion"/></a>
|
||||||
<a href="https://github.com/yuanweize" title="I"><img src="https://private-avatars.githubusercontent.com/u/30067203?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1MDAsIm5iZiI6MTczNDYyMTMwMCwicGF0aCI6Ii91LzMwMDY3MjAzIn0.VIGWXgTavmAdU4XVHO1cRjkXYV_Sz6P62Pv7DmL8KYo&v=4" width="50;" alt="I"/></a>
|
<a href="https://github.com/yuanweize" title="I"><img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/></a>
|
||||||
<a href="https://github.com/lvyaoting" title="lvyaoting"><img src="https://private-avatars.githubusercontent.com/u/166296299?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIwODAsIm5iZiI6MTczNDYyMDg4MCwicGF0aCI6Ii91LzE2NjI5NjI5OSJ9.O0tQvLriVgYaVzDgvCHSalXWCOkIqyYOcnU0phI59Jw&v=4" width="50;" alt="lvyaoting"/></a>
|
<a href="https://github.com/lvyaoting" title="lvyaoting"><img src="https://avatars.githubusercontent.com/u/166296299?v=4" width="50;" alt="lvyaoting"/></a>
|
||||||
<a href="https://github.com/lyj0309" title="lyj"><img src="https://private-avatars.githubusercontent.com/u/50474995?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIyNjAsIm5iZiI6MTczNDYyMTA2MCwicGF0aCI6Ii91LzUwNDc0OTk1In0.QrkN_Fwk2k145qL8_u_ZBmzAyAZ0OchWJXkifnweyMU&v=4" width="50;" alt="lyj"/></a>
|
<a href="https://github.com/lyj0309" title="lyj"><img src="https://avatars.githubusercontent.com/u/50474995?v=4" width="50;" alt="lyj"/></a>
|
||||||
<a href="https://github.com/unclezs" title="unclezs"><img src="https://private-avatars.githubusercontent.com/u/42318775?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzQyMzE4Nzc1In0.7HduDqs32B47XUq6Qe5sB9tBPVfVAHxkVlJc1I0fs08&v=4" width="50;" alt="unclezs"/></a>
|
<a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
||||||
<a href="https://github.com/ysicing" title="缘生"><img src="https://private-avatars.githubusercontent.com/u/8605565?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1MDAsIm5iZiI6MTczNDYyMTMwMCwicGF0aCI6Ii91Lzg2MDU1NjUifQ.6ZaOJBiniY_BZzy5ueF3DprCnQJJmJQF3pMvRWx8SnU&v=4" width="50;" alt="缘生"/></a>
|
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
||||||
<a href="https://github.com/yanhao98" title="严浩"><img src="https://private-avatars.githubusercontent.com/u/37316281?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzM3MzE2MjgxIn0.9epHB4FmQTQsrpyHo1Q9jZMkSSbHb3N-uAFIYiJxsVo&v=4" width="50;" alt="严浩"/></a>
|
<a href="https://github.com/yanhao98" title="严浩"><img src="https://avatars.githubusercontent.com/u/37316281?v=4" width="50;" alt="严浩"/></a>
|
||||||
<a href="https://github.com/arkylin" title="凌"><img src="https://private-avatars.githubusercontent.com/u/35104502?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI3NDAsIm5iZiI6MTczNDYyMTU0MCwicGF0aCI6Ii91LzM1MTA0NTAyIn0.moMUIFUiMO9vRarE_Q2JJzuFl3itBP3Pn8GXko8pQ1Q&v=4" width="50;" alt="凌"/></a>
|
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
||||||
<a href="https://github.com/yumusb" title="榆木"><img src="https://private-avatars.githubusercontent.com/u/43062104?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzQzMDYyMTA0In0.GDbdMKLSWtcMF9u2GVZwwHrYSlcv0QBD27kzEOa-tmk&v=4" width="50;" alt="榆木"/></a>
|
<a href="https://github.com/yumusb" title="榆木"><img src="https://avatars.githubusercontent.com/u/43062104?v=4" width="50;" alt="榆木"/></a>
|
||||||
<a href="https://github.com/colour93" title="玖叁"><img src="https://private-avatars.githubusercontent.com/u/64313711?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIwODAsIm5iZiI6MTczNDYyMDg4MCwicGF0aCI6Ii91LzY0MzEzNzExIn0.o_wwt9UDb5UbSL_Sad7oJ5pWv99P5z2ADuucgns4BR4&v=4" width="50;" alt="玖叁"/></a>
|
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
||||||
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://private-avatars.githubusercontent.com/u/42692274?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1MDAsIm5iZiI6MTczNDYyMTMwMCwicGF0aCI6Ii91LzQyNjkyMjc0In0.8HCQ2z_GMkIlPZLt7oFF2gejvTI2At5nsa-Q5XpzmP0&v=4" width="50;" alt="Tony"/></a>
|
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
||||||
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://private-avatars.githubusercontent.com/u/19658596?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzE5NjU4NTk2In0.N_ydo-AdSlfdZVAGTgX065iagM3thxFp6xElGmEA-2M&v=4" width="50;" alt="Tater Li"/></a>
|
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
||||||
<a href="https://github.com/IamTaoChen" title="Tao Chen"><img src="https://private-avatars.githubusercontent.com/u/42793494?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1MDAsIm5iZiI6MTczNDYyMTMwMCwicGF0aCI6Ii91LzQyNzkzNDk0In0.afMTfBX92GeSWcI5xgRchgqdK-e2oZM7_viQ3tCE2v8&v=4" width="50;" alt="Tao Chen"/></a>
|
<a href="https://github.com/IamTaoChen" title="Tao Chen"><img src="https://avatars.githubusercontent.com/u/42793494?v=4" width="50;" alt="Tao Chen"/></a>
|
||||||
<a href="https://github.com/Septrum101" title="Spetrum"><img src="https://private-avatars.githubusercontent.com/u/11692994?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1NjAsIm5iZiI6MTczNDYyMTM2MCwicGF0aCI6Ii91LzExNjkyOTk0In0.NlocagZ0a56dLOStFcfVvArSUf67Rv6KbsecZdeuuQA&v=4" width="50;" alt="Spetrum"/></a>
|
<a href="https://github.com/Septrum101" title="Spetrum"><img src="https://avatars.githubusercontent.com/u/11692994?v=4" width="50;" alt="Spetrum"/></a>
|
||||||
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://private-avatars.githubusercontent.com/u/13828658?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2ODAsIm5iZiI6MTczNDYyMTQ4MCwicGF0aCI6Ii91LzEzODI4NjU4In0.z5ahG-qE7wmOpYgbWRpcXUYr4lu7dK5hrP8s4I1PzZc&v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://avatars.githubusercontent.com/u/13828658?v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
||||||
<a href="https://github.com/silver-ymz" title="Mingzhuo Yin"><img src="https://private-avatars.githubusercontent.com/u/78400701?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIwODAsIm5iZiI6MTczNDYyMDg4MCwicGF0aCI6Ii91Lzc4NDAwNzAxIn0.N_6NmRl3_qMacx_N9XxSBGAIE8UxnBQ1KQYjX28dOsg&v=4" width="50;" alt="Mingzhuo Yin"/></a>
|
<a href="https://github.com/silver-ymz" title="Mingzhuo Yin"><img src="https://avatars.githubusercontent.com/u/78400701?v=4" width="50;" alt="Mingzhuo Yin"/></a>
|
||||||
<a href="https://github.com/MartijnLindeman" title="Martijn Lindeman"><img src="https://private-avatars.githubusercontent.com/u/78365708?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI1NjAsIm5iZiI6MTczNDYyMTM2MCwicGF0aCI6Ii91Lzc4MzY1NzA4In0.SnmleglOF7Aa-VhfMXqvoWaY1MjXFd8XRaroEykM7Rk&v=4" width="50;" alt="Martijn Lindeman"/></a>
|
<a href="https://github.com/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a>
|
||||||
<a href="https://github.com/funnyzak" title="Leon"><img src="https://private-avatars.githubusercontent.com/u/2562087?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI0NDAsIm5iZiI6MTczNDYyMTI0MCwicGF0aCI6Ii91LzI1NjIwODcifQ.WtOh1r9QTMP9YDt_m0sj6jPoWPDsJhAoV-xo34K3wb0&v=4" width="50;" alt="Leon"/></a>
|
<a href="https://github.com/funnyzak" title="Leon"><img src="https://avatars.githubusercontent.com/u/2562087?v=4" width="50;" alt="Leon"/></a>
|
||||||
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://private-avatars.githubusercontent.com/u/22239339?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI2ODAsIm5iZiI6MTczNDYyMTQ4MCwicGF0aCI6Ii91LzIyMjM5MzM5In0.393dbSCTXlwCAqkuHnlSeQewfmFmGCEnEe2heE2Cxkw&v=4" width="50;" alt="KorenKrita"/></a>
|
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a>
|
||||||
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://private-avatars.githubusercontent.com/u/1948179?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI3NDAsIm5iZiI6MTczNDYyMTU0MCwicGF0aCI6Ii91LzE5NDgxNzkifQ.oUNENBP-sxheMw4L01GJTPFnouPUuYmOLO70_vwyiIk&v=4" width="50;" alt="Ian Li"/></a>
|
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
|
||||||
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://private-avatars.githubusercontent.com/u/77104800?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI4NjAsIm5iZiI6MTczNDYyMTY2MCwicGF0aCI6Ii91Lzc3MTA0ODAwIn0.APJkWxTTlb70HZFYbngGKkzfU9xrqVCs6ZFosPfhiGU&v=4" width="50;" alt="GreenTeodoro839"/></a>
|
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
|
||||||
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://private-avatars.githubusercontent.com/u/71542548?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI4NjAsIm5iZiI6MTczNDYyMTY2MCwicGF0aCI6Ii91LzcxNTQyNTQ4In0.Unw-52ca3wsliHaYw50jsn2VYhBnzxAa2UErp56S_QU&v=4" width="50;" alt="Esdese"/></a>
|
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a>
|
||||||
<a href="https://github.com/wwng2333" title=":D"><img src="https://private-avatars.githubusercontent.com/u/17147265?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjI4MDAsIm5iZiI6MTczNDYyMTYwMCwicGF0aCI6Ii91LzE3MTQ3MjY1In0.Agl66SjSVYsKArxsl9-Q-o3b8slBtdhYPkzEjffrxbE&v=4" width="50;" alt=":D"/></a>
|
<a href="https://github.com/wwng2333" title=":D"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt=":D"/></a>
|
||||||
<a href="https://github.com/wellcoming" title="Coming"><img src="https://private-avatars.githubusercontent.com/u/74850890?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MzQ2MjIyMDAsIm5iZiI6MTczNDYyMTAwMCwicGF0aCI6Ii91Lzc0ODUwODkwIn0.WSdaOcRMJAKupA6LxWCCFBH_wjRLJKpNwb-aUvr2Ssw&v=4" width="50;" alt="Coming"/></a><!--GAMFC_DELIMITER_END-->
|
<a href="https://github.com/wellcoming" title="Coming"><img src="https://avatars.githubusercontent.com/u/74850890?v=4" width="50;" alt="Coming"/></a><!--GAMFC_DELIMITER_END-->
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database.
|
- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database.
|
||||||
|
@ -97,7 +97,7 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
|
|||||||
auth.PATCH("/notification-group/:id", commonHandler(updateNotificationGroup))
|
auth.PATCH("/notification-group/:id", commonHandler(updateNotificationGroup))
|
||||||
auth.POST("/batch-delete/notification-group", commonHandler(batchDeleteNotificationGroup))
|
auth.POST("/batch-delete/notification-group", commonHandler(batchDeleteNotificationGroup))
|
||||||
|
|
||||||
auth.GET("/server", listHandler(listServer))
|
auth.GET("/server", commonHandler(listServer))
|
||||||
auth.PATCH("/server/:id", commonHandler(updateServer))
|
auth.PATCH("/server/:id", commonHandler(updateServer))
|
||||||
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
|
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
|
||||||
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
|
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
|
||||||
|
@ -36,24 +36,22 @@ func waf(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handl
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRealIp(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
func getRealIp(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||||
var ip, connectingIp string
|
|
||||||
p, ok := peer.FromContext(ctx)
|
|
||||||
if ok {
|
|
||||||
addrPort, err := netip.ParseAddrPort(p.Addr.String())
|
|
||||||
if err == nil {
|
|
||||||
connectingIp = addrPort.Addr().String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx = context.WithValue(ctx, model.CtxKeyConnectingIP{}, connectingIp)
|
|
||||||
|
|
||||||
if singleton.Conf.RealIPHeader == "" {
|
if singleton.Conf.RealIPHeader == "" {
|
||||||
return handler(ctx, req)
|
return handler(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ip string
|
||||||
|
|
||||||
if singleton.Conf.RealIPHeader == model.ConfigUsePeerIP {
|
if singleton.Conf.RealIPHeader == model.ConfigUsePeerIP {
|
||||||
if connectingIp == "" {
|
p, ok := peer.FromContext(ctx)
|
||||||
return nil, fmt.Errorf("connecting ip not found")
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("peer not found")
|
||||||
}
|
}
|
||||||
|
addrPort, err := netip.ParseAddrPort(p.Addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ip = addrPort.Addr().String()
|
||||||
} else {
|
} else {
|
||||||
vals := metadata.ValueFromIncomingContext(ctx, singleton.Conf.RealIPHeader)
|
vals := metadata.ValueFromIncomingContext(ctx, singleton.Conf.RealIPHeader)
|
||||||
if len(vals) == 0 {
|
if len(vals) == 0 {
|
||||||
@ -67,7 +65,7 @@ func getRealIp(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if singleton.Conf.Debug {
|
if singleton.Conf.Debug {
|
||||||
log.Printf("NEZHA>> gRPC Agent Real IP: %s, connecting IP: %s\n", ip, connectingIp)
|
log.Printf("NEZHA>> gRPC Real IP: %s", ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, model.CtxKeyRealIP{}, ip)
|
ctx = context.WithValue(ctx, model.CtxKeyRealIP{}, ip)
|
||||||
|
12
go.mod
12
go.mod
@ -1,8 +1,8 @@
|
|||||||
module github.com/nezhahq/nezha
|
module github.com/nezhahq/nezha
|
||||||
|
|
||||||
go 1.23.0
|
go 1.22.7
|
||||||
|
|
||||||
toolchain go1.23.2
|
toolchain go1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/appleboy/gin-jwt/v2 v2.10.0
|
github.com/appleboy/gin-jwt/v2 v2.10.0
|
||||||
@ -31,11 +31,11 @@ require (
|
|||||||
github.com/swaggo/swag v1.16.4
|
github.com/swaggo/swag v1.16.4
|
||||||
github.com/tidwall/gjson v1.18.0
|
github.com/tidwall/gjson v1.18.0
|
||||||
golang.org/x/crypto v0.31.0
|
golang.org/x/crypto v0.31.0
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884
|
||||||
golang.org/x/net v0.33.0
|
golang.org/x/net v0.32.0
|
||||||
golang.org/x/sync v0.10.0
|
golang.org/x/sync v0.10.0
|
||||||
google.golang.org/grpc v1.69.2
|
google.golang.org/grpc v1.69.0
|
||||||
google.golang.org/protobuf v1.36.0
|
google.golang.org/protobuf v1.35.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gorm.io/driver/sqlite v1.5.7
|
gorm.io/driver/sqlite v1.5.7
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
|
16
go.sum
16
go.sum
@ -187,8 +187,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
|
||||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
@ -196,8 +196,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
@ -228,10 +228,10 @@ golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw
|
|||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
|
||||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
@ -19,7 +19,7 @@ type AlertRule struct {
|
|||||||
NotificationGroupID uint64 `json:"notification_group_id"` // 该报警规则所在的通知组
|
NotificationGroupID uint64 `json:"notification_group_id"` // 该报警规则所在的通知组
|
||||||
FailTriggerTasksRaw string `gorm:"default:'[]'" json:"-"`
|
FailTriggerTasksRaw string `gorm:"default:'[]'" json:"-"`
|
||||||
RecoverTriggerTasksRaw string `gorm:"default:'[]'" json:"-"`
|
RecoverTriggerTasksRaw string `gorm:"default:'[]'" json:"-"`
|
||||||
Rules []*Rule `gorm:"-" json:"rules"`
|
Rules []Rule `gorm:"-" json:"rules"`
|
||||||
FailTriggerTasks []uint64 `gorm:"-" json:"fail_trigger_tasks"` // 失败时执行的触发任务id
|
FailTriggerTasks []uint64 `gorm:"-" json:"fail_trigger_tasks"` // 失败时执行的触发任务id
|
||||||
RecoverTriggerTasks []uint64 `gorm:"-" json:"recover_trigger_tasks"` // 恢复时执行的触发任务id
|
RecoverTriggerTasks []uint64 `gorm:"-" json:"recover_trigger_tasks"` // 恢复时执行的触发任务id
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package model
|
|||||||
|
|
||||||
type AlertRuleForm struct {
|
type AlertRuleForm struct {
|
||||||
Name string `json:"name" minLength:"1"`
|
Name string `json:"name" minLength:"1"`
|
||||||
Rules []*Rule `json:"rules"`
|
Rules []Rule `json:"rules"`
|
||||||
FailTriggerTasks []uint64 `json:"fail_trigger_tasks"` // 失败时触发的任务id
|
FailTriggerTasks []uint64 `json:"fail_trigger_tasks"` // 失败时触发的任务id
|
||||||
RecoverTriggerTasks []uint64 `json:"recover_trigger_tasks"` // 恢复时触发的任务id
|
RecoverTriggerTasks []uint64 `json:"recover_trigger_tasks"` // 恢复时触发的任务id
|
||||||
NotificationGroupID uint64 `json:"notification_group_id"`
|
NotificationGroupID uint64 `json:"notification_group_id"`
|
||||||
|
@ -12,7 +12,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CtxKeyRealIP struct{}
|
type CtxKeyRealIP struct{}
|
||||||
type CtxKeyConnectingIP struct{}
|
|
||||||
|
|
||||||
type Common struct {
|
type Common struct {
|
||||||
ID uint64 `gorm:"primaryKey" json:"id,omitempty"`
|
ID uint64 `gorm:"primaryKey" json:"id,omitempty"`
|
||||||
@ -28,10 +27,6 @@ func (c *Common) GetID() uint64 {
|
|||||||
return c.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Common) GetUserID() uint64 {
|
|
||||||
return c.UserID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Common) HasPermission(ctx *gin.Context) bool {
|
func (c *Common) HasPermission(ctx *gin.Context) bool {
|
||||||
auth, ok := ctx.Get(CtxKeyAuthorizedUser)
|
auth, ok := ctx.Get(CtxKeyAuthorizedUser)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -48,21 +43,9 @@ func (c *Common) HasPermission(ctx *gin.Context) bool {
|
|||||||
|
|
||||||
type CommonInterface interface {
|
type CommonInterface interface {
|
||||||
GetID() uint64
|
GetID() uint64
|
||||||
GetUserID() uint64
|
|
||||||
HasPermission(*gin.Context) bool
|
HasPermission(*gin.Context) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindUserID[S ~[]E, E CommonInterface](s S, uid uint64) []uint64 {
|
|
||||||
var list []uint64
|
|
||||||
for _, v := range s {
|
|
||||||
if v.GetUserID() == uid {
|
|
||||||
list = append(list, v.GetID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Code int `json:"code,omitempty"`
|
Code int `json:"code,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
|
@ -16,7 +16,6 @@ var Languages = map[string]string{
|
|||||||
"zh_TW": "繁體中文",
|
"zh_TW": "繁體中文",
|
||||||
"en_US": "English",
|
"en_US": "English",
|
||||||
"es_ES": "Español",
|
"es_ES": "Español",
|
||||||
"de_DE": "Deutsch",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Localizer struct {
|
type Localizer struct {
|
||||||
|
Binary file not shown.
@ -1,228 +0,0 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
||||||
#
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2024-11-23 23:56+0800\n"
|
|
||||||
"PO-Revision-Date: 2024-12-17 04:52+0000\n"
|
|
||||||
"Last-Translator: UUBulb <uub@kuzu.uk>\n"
|
|
||||||
"Language-Team: German <https://hosted.weblate.org/projects/nezha/"
|
|
||||||
"nezha-dashboard/de/>\n"
|
|
||||||
"Language: de_DE\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
|
||||||
"X-Generator: Weblate 5.9\n"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:100
|
|
||||||
#, c-format
|
|
||||||
msgid "alert id %d does not exist"
|
|
||||||
msgstr "benachrichtigungs ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:155
|
|
||||||
msgid "duration need to be at least 3"
|
|
||||||
msgstr "dauer muss mindestens 3 sein"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:159
|
|
||||||
msgid "cycle_interval need to be at least 1"
|
|
||||||
msgstr "cycle_interval muss mindestens 1 sein"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:162
|
|
||||||
msgid "cycle_start is not set"
|
|
||||||
msgstr "cycle_start ist nicht eingestellt"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:165
|
|
||||||
msgid "cycle_start is a future value"
|
|
||||||
msgstr "cycle_start ist ein zukünftiger wert"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:170
|
|
||||||
msgid "need to configure at least a single rule"
|
|
||||||
msgstr "mindestens eine Regel muss konfiguriert sein"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/controller.go:195
|
|
||||||
msgid "database error"
|
|
||||||
msgstr "datenbankfehler"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/cron.go:63 cmd/dashboard/controller/cron.go:122
|
|
||||||
msgid "scheduled tasks cannot be triggered by alarms"
|
|
||||||
msgstr "geplante aufgaben können nicht durch Alarme ausgelöst werden"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/cron.go:161
|
|
||||||
#, c-format
|
|
||||||
msgid "task id %d does not exist"
|
|
||||||
msgstr "task ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:56 cmd/dashboard/controller/ddns.go:120
|
|
||||||
msgid "the retry count must be an integer between 1 and 10"
|
|
||||||
msgstr "der retry_count muss eine Zahl zwischen 1 und 10 sein"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:79 cmd/dashboard/controller/ddns.go:148
|
|
||||||
msgid "error parsing %s: %v"
|
|
||||||
msgstr "fehler beim parsen von %s: %v"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:125 cmd/dashboard/controller/nat.go:95
|
|
||||||
#, c-format
|
|
||||||
msgid "profile id %d does not exist"
|
|
||||||
msgstr "profil ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/fm.go:45 cmd/dashboard/controller/terminal.go:43
|
|
||||||
msgid "server not found or not connected"
|
|
||||||
msgstr "server nicht gefunden oder nicht verbunden"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification.go:67
|
|
||||||
#: cmd/dashboard/controller/notification.go:125
|
|
||||||
msgid "a test message"
|
|
||||||
msgstr "testnachricht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification.go:106
|
|
||||||
#, c-format
|
|
||||||
msgid "notification id %d does not exist"
|
|
||||||
msgstr "benachrichtigung ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:80
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:142
|
|
||||||
msgid "have invalid notification id"
|
|
||||||
msgstr "haben ungültige Benachrichtigungs ID"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:131
|
|
||||||
#: cmd/dashboard/controller/server_group.go:130
|
|
||||||
#, c-format
|
|
||||||
msgid "group id %d does not exist"
|
|
||||||
msgstr "gruppen ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/server.go:60
|
|
||||||
#, c-format
|
|
||||||
msgid "server id %d does not exist"
|
|
||||||
msgstr "server ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/server_group.go:78
|
|
||||||
#: cmd/dashboard/controller/server_group.go:139
|
|
||||||
msgid "have invalid server id"
|
|
||||||
msgstr "haben ungültige Server ID"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:79
|
|
||||||
#: cmd/dashboard/controller/service.go:155
|
|
||||||
msgid "server not found"
|
|
||||||
msgstr "server nicht gefunden"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:86 cmd/dashboard/controller/user.go:23
|
|
||||||
msgid "unauthorized"
|
|
||||||
msgstr "nicht autorisiert"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:247
|
|
||||||
#, c-format
|
|
||||||
msgid "service id %d does not exist"
|
|
||||||
msgstr "service ID %d existiert nicht"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/user.go:66
|
|
||||||
msgid "password length must be greater than 6"
|
|
||||||
msgstr "passwort muss länger als 6 Zeichen sein"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/user.go:69
|
|
||||||
msgid "username can't be empty"
|
|
||||||
msgstr "benutzername darf nicht leer sein"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:122
|
|
||||||
msgid "timeout: no connection established"
|
|
||||||
msgstr "timeout: Keine Verbindung hergestellt"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:125
|
|
||||||
msgid "timeout: user connection not established"
|
|
||||||
msgstr "timeout: Benutzerverbindung nicht etabliert"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:128
|
|
||||||
msgid "timeout: agent connection not established"
|
|
||||||
msgstr "timeout: Agent-Verbindung nicht etabliert"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:58
|
|
||||||
msgid "Scheduled Task Executed Successfully"
|
|
||||||
msgstr "geplante Aufgabe erfolgreich ausgeführt"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:62
|
|
||||||
msgid "Scheduled Task Executed Failed"
|
|
||||||
msgstr "geplante Aufgabe fehlgeschlagen"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:217
|
|
||||||
msgid "IP Changed"
|
|
||||||
msgstr "IP geändert"
|
|
||||||
|
|
||||||
#: service/singleton/alertsentinel.go:159
|
|
||||||
msgid "Incident"
|
|
||||||
msgstr "Vorfall"
|
|
||||||
|
|
||||||
#: service/singleton/alertsentinel.go:169
|
|
||||||
msgid "Resolved"
|
|
||||||
msgstr "Gelöst"
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:53
|
|
||||||
msgid "Tasks failed to register: ["
|
|
||||||
msgstr "Aufgaben konnten nicht registriert werden: ["
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:60
|
|
||||||
msgid ""
|
|
||||||
"] These tasks will not execute properly. Fix them in the admin dashboard."
|
|
||||||
msgstr ""
|
|
||||||
"] Diese Aufgaben werden nicht korrekt ausgeführt. Reparieren Sie diese im "
|
|
||||||
"Admin-Dashboard."
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:146 service/singleton/crontask.go:171
|
|
||||||
#, c-format
|
|
||||||
msgid "[Task failed] %s: server %s is offline and cannot execute the task"
|
|
||||||
msgstr ""
|
|
||||||
"[Aufgabe fehlgeschlagen] %s: Server %s ist offline und kann die Aufgabe "
|
|
||||||
"nicht ausführen"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:439
|
|
||||||
#, c-format
|
|
||||||
msgid "[Latency] %s %2f > %2f, Reporter: %s"
|
|
||||||
msgstr "[Latency] %s %2f > %2f, Reporter: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:446
|
|
||||||
#, c-format
|
|
||||||
msgid "[Latency] %s %2f < %2f, Reporter: %s"
|
|
||||||
msgstr "[Latency] %s %2f < %2f, Reporter: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:472
|
|
||||||
#, c-format
|
|
||||||
msgid "[%s] %s Reporter: %s, Error: %s"
|
|
||||||
msgstr "[%s] %s Reporter: %s, Fehler: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:515
|
|
||||||
#, c-format
|
|
||||||
msgid "[TLS] Fetch cert info failed, Reporter: %s, Error: %s"
|
|
||||||
msgstr "[TLS] Fetch cert info gescheitert, Reporter: %s, Fehler: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:555
|
|
||||||
#, c-format
|
|
||||||
msgid "The TLS certificate will expire within seven days. Expiration time: %s"
|
|
||||||
msgstr "Das TLS-Zertifikat läuft innerhalb von sieben Tagen ab. Ablaufzeit: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:568
|
|
||||||
#, c-format
|
|
||||||
msgid ""
|
|
||||||
"TLS certificate changed, old: issuer %s, expires at %s; new: issuer %s, "
|
|
||||||
"expires at %s"
|
|
||||||
msgstr ""
|
|
||||||
"TLS-Zertifikat geändert, alt: Emittent %s, läuft ab bei %s; neu: Emittent "
|
|
||||||
"%s, läuft ab bei %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:604
|
|
||||||
msgid "No Data"
|
|
||||||
msgstr "Keine Daten"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:606
|
|
||||||
msgid "Good"
|
|
||||||
msgstr "Gut"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:608
|
|
||||||
msgid "Low Availability"
|
|
||||||
msgstr "Niedere Verfügbarkeit"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:610
|
|
||||||
msgid "Down"
|
|
||||||
msgstr "Unten"
|
|
Binary file not shown.
@ -1,232 +0,0 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
||||||
#
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2024-11-23 23:56+0800\n"
|
|
||||||
"PO-Revision-Date: 2024-12-17 04:52+0000\n"
|
|
||||||
"Last-Translator: UUBulb <uub@kuzu.uk>\n"
|
|
||||||
"Language-Team: Spanish <https://hosted.weblate.org/projects/nezha/"
|
|
||||||
"nezha-dashboard/es/>\n"
|
|
||||||
"Language: es_ES\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
|
||||||
"X-Generator: Weblate 5.9\n"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:100
|
|
||||||
#, c-format
|
|
||||||
msgid "alert id %d does not exist"
|
|
||||||
msgstr "el ID de alerta %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:155
|
|
||||||
msgid "duration need to be at least 3"
|
|
||||||
msgstr "la duración debe ser al menos de 3"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:159
|
|
||||||
msgid "cycle_interval need to be at least 1"
|
|
||||||
msgstr "cycle_interval debe ser al menos 1"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:162
|
|
||||||
msgid "cycle_start is not set"
|
|
||||||
msgstr "no se ha configurado el cycle_start"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:165
|
|
||||||
msgid "cycle_start is a future value"
|
|
||||||
msgstr "cycle_start es un valor futuro"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/alertrule.go:170
|
|
||||||
msgid "need to configure at least a single rule"
|
|
||||||
msgstr "es necesario configurar al menos una regla"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/controller.go:195
|
|
||||||
msgid "database error"
|
|
||||||
msgstr "error de base de datos"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/cron.go:63 cmd/dashboard/controller/cron.go:122
|
|
||||||
msgid "scheduled tasks cannot be triggered by alarms"
|
|
||||||
msgstr "las tareas programadas no pueden ser activadas por alarmas"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/cron.go:161
|
|
||||||
#, c-format
|
|
||||||
msgid "task id %d does not exist"
|
|
||||||
msgstr "el ID de la tarea %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:56 cmd/dashboard/controller/ddns.go:120
|
|
||||||
msgid "the retry count must be an integer between 1 and 10"
|
|
||||||
msgstr "el número de reintentos debe ser un número entero entre 1 y 10"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:79 cmd/dashboard/controller/ddns.go:148
|
|
||||||
msgid "error parsing %s: %v"
|
|
||||||
msgstr "error al analizar %s: %v"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/ddns.go:125 cmd/dashboard/controller/nat.go:95
|
|
||||||
#, c-format
|
|
||||||
msgid "profile id %d does not exist"
|
|
||||||
msgstr "el ID de perfil %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/fm.go:45 cmd/dashboard/controller/terminal.go:43
|
|
||||||
msgid "server not found or not connected"
|
|
||||||
msgstr "servidor no encontrado o no conectado"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification.go:67
|
|
||||||
#: cmd/dashboard/controller/notification.go:125
|
|
||||||
msgid "a test message"
|
|
||||||
msgstr "un mensaje de prueba"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification.go:106
|
|
||||||
#, c-format
|
|
||||||
msgid "notification id %d does not exist"
|
|
||||||
msgstr "el ID de notificación %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:80
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:142
|
|
||||||
msgid "have invalid notification id"
|
|
||||||
msgstr "hay un ID de notificación no válido"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/notification_group.go:131
|
|
||||||
#: cmd/dashboard/controller/server_group.go:130
|
|
||||||
#, c-format
|
|
||||||
msgid "group id %d does not exist"
|
|
||||||
msgstr "el ID de grupo %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/server.go:60
|
|
||||||
#, c-format
|
|
||||||
msgid "server id %d does not exist"
|
|
||||||
msgstr "el ID de servidor %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/server_group.go:78
|
|
||||||
#: cmd/dashboard/controller/server_group.go:139
|
|
||||||
msgid "have invalid server id"
|
|
||||||
msgstr "hay un ID de servidor no válido"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:79
|
|
||||||
#: cmd/dashboard/controller/service.go:155
|
|
||||||
msgid "server not found"
|
|
||||||
msgstr "servidor no encontrado"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:86 cmd/dashboard/controller/user.go:23
|
|
||||||
msgid "unauthorized"
|
|
||||||
msgstr "no autorizado"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/service.go:247
|
|
||||||
#, c-format
|
|
||||||
msgid "service id %d does not exist"
|
|
||||||
msgstr "el ID de servicio %d no existe"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/user.go:66
|
|
||||||
msgid "password length must be greater than 6"
|
|
||||||
msgstr "la longitud de la contraseña debe ser mayor a 6"
|
|
||||||
|
|
||||||
#: cmd/dashboard/controller/user.go:69
|
|
||||||
msgid "username can't be empty"
|
|
||||||
msgstr "el nombre de usuario no puede estar vacío"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:122
|
|
||||||
msgid "timeout: no connection established"
|
|
||||||
msgstr "tiempo de espera agotado: no se pudo establecer conexión"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:125
|
|
||||||
msgid "timeout: user connection not established"
|
|
||||||
msgstr "tiempo de espera agotado: no se pudo establecer conexión con el usuario"
|
|
||||||
|
|
||||||
#: service/rpc/io_stream.go:128
|
|
||||||
msgid "timeout: agent connection not established"
|
|
||||||
msgstr "tiempo de espera agotado: no se pudo establecer conexión con agent"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:58
|
|
||||||
msgid "Scheduled Task Executed Successfully"
|
|
||||||
msgstr "tarea programada ejecutada con éxito"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:62
|
|
||||||
msgid "Scheduled Task Executed Failed"
|
|
||||||
msgstr "falló la ejecución de la tarea programada"
|
|
||||||
|
|
||||||
#: service/rpc/nezha.go:217
|
|
||||||
msgid "IP Changed"
|
|
||||||
msgstr "IP cambiada"
|
|
||||||
|
|
||||||
#: service/singleton/alertsentinel.go:159
|
|
||||||
msgid "Incident"
|
|
||||||
msgstr "Incidente"
|
|
||||||
|
|
||||||
#: service/singleton/alertsentinel.go:169
|
|
||||||
msgid "Resolved"
|
|
||||||
msgstr "Resuelto"
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:53
|
|
||||||
msgid "Tasks failed to register: ["
|
|
||||||
msgstr "Las tareas no se pudieron registrar: ["
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:60
|
|
||||||
msgid ""
|
|
||||||
"] These tasks will not execute properly. Fix them in the admin dashboard."
|
|
||||||
msgstr ""
|
|
||||||
"] Estas tareas no se ejecutarán correctamente. Corríjalas en el dashboard de "
|
|
||||||
"administración."
|
|
||||||
|
|
||||||
#: service/singleton/crontask.go:146 service/singleton/crontask.go:171
|
|
||||||
#, c-format
|
|
||||||
msgid "[Task failed] %s: server %s is offline and cannot execute the task"
|
|
||||||
msgstr ""
|
|
||||||
"[Tarea fallida] %s: el servidor %s está fuera de línea y no puede ejecutar "
|
|
||||||
"la tarea"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:439
|
|
||||||
#, c-format
|
|
||||||
msgid "[Latency] %s %2f > %2f, Reporter: %s"
|
|
||||||
msgstr "[Latencia] %s %2f > %2f, Reportado por: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:446
|
|
||||||
#, c-format
|
|
||||||
msgid "[Latency] %s %2f < %2f, Reporter: %s"
|
|
||||||
msgstr "[Latencia] %s %2f < %2f, Reportado por: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:472
|
|
||||||
#, c-format
|
|
||||||
msgid "[%s] %s Reporter: %s, Error: %s"
|
|
||||||
msgstr "[%s] %s Reportado por: %s, Error: %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:515
|
|
||||||
#, c-format
|
|
||||||
msgid "[TLS] Fetch cert info failed, Reporter: %s, Error: %s"
|
|
||||||
msgstr ""
|
|
||||||
"[TLS] Error al obtener información del certificado, Reportado por: %s, Error:"
|
|
||||||
" %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:555
|
|
||||||
#, c-format
|
|
||||||
msgid "The TLS certificate will expire within seven days. Expiration time: %s"
|
|
||||||
msgstr ""
|
|
||||||
"El certificado TLS expirará en los próximos siete días. Fecha de expiración: "
|
|
||||||
"%s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:568
|
|
||||||
#, c-format
|
|
||||||
msgid ""
|
|
||||||
"TLS certificate changed, old: issuer %s, expires at %s; new: issuer %s, "
|
|
||||||
"expires at %s"
|
|
||||||
msgstr ""
|
|
||||||
"El certificado TLS ha cambiado. Antiguo: emisor %s, expira en %s; Nuevo: "
|
|
||||||
"emisor %s, expira en %s"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:604
|
|
||||||
msgid "No Data"
|
|
||||||
msgstr "Sin datos"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:606
|
|
||||||
msgid "Good"
|
|
||||||
msgstr "Bueno"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:608
|
|
||||||
msgid "Low Availability"
|
|
||||||
msgstr "Baja disponibilidad"
|
|
||||||
|
|
||||||
#: service/singleton/servicesentinel.go:610
|
|
||||||
msgid "Down"
|
|
||||||
msgstr "Fallo"
|
|
@ -3,12 +3,10 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"maps"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -147,8 +145,3 @@ func Itoa[T constraints.Integer](i T) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapValuesToSlice[Map ~map[K]V, K comparable, V any](m Map) []V {
|
|
||||||
s := make([]V, 0, len(m))
|
|
||||||
return slices.AppendSeq(s, maps.Values(m))
|
|
||||||
}
|
|
||||||
|
@ -201,17 +201,8 @@ func (s *NezhaHandler) ReportGeoIP(c context.Context, r *pb.GeoIP) (*pb.GeoIP, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
geoip := model.PB2GeoIP(r)
|
geoip := model.PB2GeoIP(r)
|
||||||
use6 := r.GetUse6()
|
|
||||||
|
|
||||||
if geoip.IP.IPv4Addr == "" && geoip.IP.IPv6Addr == "" {
|
|
||||||
ip, _ := c.Value(model.CtxKeyRealIP{}).(string)
|
|
||||||
if ip == "" {
|
|
||||||
ip, _ = c.Value(model.CtxKeyConnectingIP{}).(string)
|
|
||||||
}
|
|
||||||
geoip.IP.IPv4Addr = ip
|
|
||||||
}
|
|
||||||
|
|
||||||
joinedIP := geoip.IP.Join()
|
joinedIP := geoip.IP.Join()
|
||||||
|
use6 := r.GetUse6()
|
||||||
|
|
||||||
singleton.ServerLock.RLock()
|
singleton.ServerLock.RLock()
|
||||||
// 检查并更新DDNS
|
// 检查并更新DDNS
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
pb "github.com/nezhahq/nezha/proto"
|
pb "github.com/nezhahq/nezha/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,7 +79,10 @@ func UpdateCronList() {
|
|||||||
CronLock.RLock()
|
CronLock.RLock()
|
||||||
defer CronLock.RUnlock()
|
defer CronLock.RUnlock()
|
||||||
|
|
||||||
CronList = utils.MapValuesToSlice(Crons)
|
CronList = make([]*model.Cron, 0, len(Crons))
|
||||||
|
for _, c := range Crons {
|
||||||
|
CronList = append(CronList, c)
|
||||||
|
}
|
||||||
slices.SortFunc(CronList, func(a, b *model.Cron) int {
|
slices.SortFunc(CronList, func(a, b *model.Cron) int {
|
||||||
return cmp.Compare(a.ID, b.ID)
|
return cmp.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
ddns2 "github.com/nezhahq/nezha/pkg/ddns"
|
ddns2 "github.com/nezhahq/nezha/pkg/ddns"
|
||||||
"github.com/nezhahq/nezha/pkg/ddns/dummy"
|
"github.com/nezhahq/nezha/pkg/ddns/dummy"
|
||||||
"github.com/nezhahq/nezha/pkg/ddns/webhook"
|
"github.com/nezhahq/nezha/pkg/ddns/webhook"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -55,7 +54,10 @@ func UpdateDDNSList() {
|
|||||||
DDNSListLock.Lock()
|
DDNSListLock.Lock()
|
||||||
defer DDNSListLock.Unlock()
|
defer DDNSListLock.Unlock()
|
||||||
|
|
||||||
DDNSList = utils.MapValuesToSlice(DDNSCache)
|
DDNSList = make([]*model.DDNSProfile, 0, len(DDNSCache))
|
||||||
|
for _, p := range DDNSCache {
|
||||||
|
DDNSList = append(DDNSList, p)
|
||||||
|
}
|
||||||
slices.SortFunc(DDNSList, func(a, b *model.DDNSProfile) int {
|
slices.SortFunc(DDNSList, func(a, b *model.DDNSProfile) int {
|
||||||
return cmp.Compare(a.ID, b.ID)
|
return cmp.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
|
@ -2,17 +2,17 @@
|
|||||||
name: "OfficialAdmin"
|
name: "OfficialAdmin"
|
||||||
repository: "https://github.com/nezhahq/admin-frontend"
|
repository: "https://github.com/nezhahq/admin-frontend"
|
||||||
author: "nezhahq"
|
author: "nezhahq"
|
||||||
version: "v1.2.2"
|
version: "v1.2.0"
|
||||||
isadmin: true
|
isadmin: true
|
||||||
isofficial: true
|
isofficial: true
|
||||||
- path: "user-dist"
|
- path: "user-dist"
|
||||||
name: "Official"
|
name: "Official"
|
||||||
repository: "https://github.com/hamster1963/nezha-dash-v1"
|
repository: "https://github.com/hamster1963/nezha-dash-v1"
|
||||||
author: "hamster1963"
|
author: "hamster1963"
|
||||||
version: "v1.6.1"
|
version: "v1.3.5"
|
||||||
isofficial: true
|
isofficial: true
|
||||||
- path: "nazhua-dist"
|
- path: "nazhua-dist"
|
||||||
name: "Nazhua"
|
name: "Nazhua"
|
||||||
repository: "https://github.com/hi2shark/nazhua"
|
repository: "https://github.com/hi2shark/nazhua"
|
||||||
author: "hi2hi"
|
author: "hi2hi"
|
||||||
version: "v0.4.22"
|
version: "v0.4.20"
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -58,7 +57,10 @@ func UpdateNATList() {
|
|||||||
NATListLock.Lock()
|
NATListLock.Lock()
|
||||||
defer NATListLock.Unlock()
|
defer NATListLock.Unlock()
|
||||||
|
|
||||||
NATList = utils.MapValuesToSlice(NATCache)
|
NATList = make([]*model.NAT, 0, len(NATCache))
|
||||||
|
for _, n := range NATCache {
|
||||||
|
NATList = append(NATList, n)
|
||||||
|
}
|
||||||
slices.SortFunc(NATList, func(a, b *model.NAT) int {
|
slices.SortFunc(NATList, func(a, b *model.NAT) int {
|
||||||
return cmp.Compare(a.ID, b.ID)
|
return cmp.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -82,7 +81,10 @@ func UpdateNotificationList() {
|
|||||||
NotificationSortedLock.Lock()
|
NotificationSortedLock.Lock()
|
||||||
defer NotificationSortedLock.Unlock()
|
defer NotificationSortedLock.Unlock()
|
||||||
|
|
||||||
NotificationListSorted = utils.MapValuesToSlice(NotificationMap)
|
NotificationListSorted = make([]*model.Notification, 0, len(NotificationMap))
|
||||||
|
for _, n := range NotificationMap {
|
||||||
|
NotificationListSorted = append(NotificationListSorted, n)
|
||||||
|
}
|
||||||
slices.SortFunc(NotificationListSorted, func(a, b *model.Notification) int {
|
slices.SortFunc(NotificationListSorted, func(a, b *model.Notification) int {
|
||||||
return cmp.Compare(a.ID, b.ID)
|
return cmp.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package singleton
|
package singleton
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"sort"
|
||||||
"slices"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -47,21 +45,29 @@ func ReSortServer() {
|
|||||||
SortedServerLock.Lock()
|
SortedServerLock.Lock()
|
||||||
defer SortedServerLock.Unlock()
|
defer SortedServerLock.Unlock()
|
||||||
|
|
||||||
SortedServerList = utils.MapValuesToSlice(ServerList)
|
SortedServerList = make([]*model.Server, 0, len(ServerList))
|
||||||
// 按照服务器 ID 排序的具体实现(ID越大越靠前)
|
SortedServerListForGuest = make([]*model.Server, 0)
|
||||||
slices.SortStableFunc(SortedServerList, func(a, b *model.Server) int {
|
for _, s := range ServerList {
|
||||||
if a.DisplayIndex == b.DisplayIndex {
|
SortedServerList = append(SortedServerList, s)
|
||||||
return cmp.Compare(a.ID, b.ID)
|
|
||||||
}
|
|
||||||
return cmp.Compare(b.DisplayIndex, a.DisplayIndex)
|
|
||||||
})
|
|
||||||
|
|
||||||
SortedServerListForGuest = make([]*model.Server, 0, len(SortedServerList))
|
|
||||||
for _, s := range SortedServerList {
|
|
||||||
if !s.HideForGuest {
|
if !s.HideForGuest {
|
||||||
SortedServerListForGuest = append(SortedServerListForGuest, s)
|
SortedServerListForGuest = append(SortedServerListForGuest, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按照服务器 ID 排序的具体实现(ID越大越靠前)
|
||||||
|
sort.SliceStable(SortedServerList, func(i, j int) bool {
|
||||||
|
if SortedServerList[i].DisplayIndex == SortedServerList[j].DisplayIndex {
|
||||||
|
return SortedServerList[i].ID < SortedServerList[j].ID
|
||||||
|
}
|
||||||
|
return SortedServerList[i].DisplayIndex > SortedServerList[j].DisplayIndex
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.SliceStable(SortedServerListForGuest, func(i, j int) bool {
|
||||||
|
if SortedServerListForGuest[i].DisplayIndex == SortedServerListForGuest[j].DisplayIndex {
|
||||||
|
return SortedServerListForGuest[i].ID < SortedServerListForGuest[j].ID
|
||||||
|
}
|
||||||
|
return SortedServerListForGuest[i].DisplayIndex > SortedServerListForGuest[j].DisplayIndex
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnServerDelete(sid []uint64) {
|
func OnServerDelete(sid []uint64) {
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"github.com/nezhahq/nezha/pkg/utils"
|
|
||||||
pb "github.com/nezhahq/nezha/proto"
|
pb "github.com/nezhahq/nezha/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -175,7 +174,11 @@ func (ss *ServiceSentinel) UpdateServiceList() {
|
|||||||
ss.ServiceListLock.Lock()
|
ss.ServiceListLock.Lock()
|
||||||
defer ss.ServiceListLock.Unlock()
|
defer ss.ServiceListLock.Unlock()
|
||||||
|
|
||||||
ss.ServiceList = utils.MapValuesToSlice(ss.Services)
|
ss.ServiceList = make([]*model.Service, 0, len(ss.Services))
|
||||||
|
for _, v := range ss.Services {
|
||||||
|
ss.ServiceList = append(ss.ServiceList, v)
|
||||||
|
}
|
||||||
|
|
||||||
slices.SortFunc(ss.ServiceList, func(a, b *model.Service) int {
|
slices.SortFunc(ss.ServiceList, func(a, b *model.Service) int {
|
||||||
return cmp.Compare(a.ID, b.ID)
|
return cmp.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/nezhahq/nezha/model"
|
"github.com/nezhahq/nezha/model"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -47,63 +46,9 @@ func OnUserDelete(id []uint64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
cron bool
|
|
||||||
server bool
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, uid := range id {
|
for _, uid := range id {
|
||||||
secret := UserIdToAgentSecret[uid]
|
secret := UserIdToAgentSecret[uid]
|
||||||
delete(AgentSecretToUserId, secret)
|
delete(AgentSecretToUserId, secret)
|
||||||
delete(UserIdToAgentSecret, uid)
|
delete(UserIdToAgentSecret, uid)
|
||||||
|
|
||||||
CronLock.RLock()
|
|
||||||
crons := model.FindUserID(CronList, uid)
|
|
||||||
CronLock.RUnlock()
|
|
||||||
|
|
||||||
cron = len(crons) > 0
|
|
||||||
if cron {
|
|
||||||
DB.Unscoped().Delete(&model.Cron{}, "id in (?)", crons)
|
|
||||||
OnDeleteCron(crons)
|
|
||||||
}
|
|
||||||
|
|
||||||
SortedServerLock.RLock()
|
|
||||||
servers := model.FindUserID(SortedServerList, uid)
|
|
||||||
SortedServerLock.RUnlock()
|
|
||||||
|
|
||||||
server = len(servers) > 0
|
|
||||||
if server {
|
|
||||||
DB.Transaction(func(tx *gorm.DB) error {
|
|
||||||
if err := tx.Unscoped().Delete(&model.Server{}, "id in (?)", servers).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tx.Unscoped().Delete(&model.ServerGroupServer{}, "server_id in (?)", servers).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
AlertsLock.Lock()
|
|
||||||
for _, sid := range servers {
|
|
||||||
for _, alert := range Alerts {
|
|
||||||
if AlertsCycleTransferStatsStore[alert.ID] != nil {
|
|
||||||
delete(AlertsCycleTransferStatsStore[alert.ID].ServerName, sid)
|
|
||||||
delete(AlertsCycleTransferStatsStore[alert.ID].Transfer, sid)
|
|
||||||
delete(AlertsCycleTransferStatsStore[alert.ID].NextUpdate, sid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DB.Unscoped().Delete(&model.Transfer{}, "server_id in (?)", servers)
|
|
||||||
AlertsLock.Unlock()
|
|
||||||
OnServerDelete(servers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cron {
|
|
||||||
UpdateCronList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if server {
|
|
||||||
ReSortServer()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user