Sfoglia il codice sorgente

Merge pull request #2090 from sspanel-uim/dev

Dev 20230725
M1Screw 2 anni fa
parent
commit
63c92418c0
44 ha cambiato i file con 579 aggiunte e 632 eliminazioni
  1. 4 3
      composer.json
  2. 317 365
      composer.lock
  3. 0 4
      config/.config.example.php
  4. 4 4
      resources/views/tabler/admin/setting/captcha.tpl
  5. 6 6
      resources/views/tabler/admin/setting/cron.tpl
  6. 1 1
      resources/views/tabler/admin/setting/email.tpl
  7. 5 5
      resources/views/tabler/admin/setting/feature.tpl
  8. 18 18
      resources/views/tabler/admin/setting/im.tpl
  9. 3 3
      resources/views/tabler/admin/setting/reg.tpl
  10. 5 5
      resources/views/tabler/admin/setting/sub.tpl
  11. 2 2
      resources/views/tabler/admin/setting/support.tpl
  12. 1 1
      resources/views/tabler/user/detect/index.tpl
  13. 1 1
      resources/views/tabler/user/edit.tpl
  14. 3 4
      resources/views/tabler/user/index.tpl
  15. 3 7
      resources/views/tabler/user/invite.tpl
  16. 1 1
      resources/views/tabler/user/money.tpl
  17. 0 8
      resources/views/tabler/user/server.tpl
  18. 14 17
      src/Command/Tool.php
  19. 2 15
      src/Controllers/Admin/DocsController.php
  20. 2 2
      src/Controllers/Admin/InviteController.php
  21. 1 4
      src/Controllers/Admin/IpController.php
  22. 0 2
      src/Controllers/Admin/SubscribeLogController.php
  23. 2 14
      src/Controllers/Admin/TicketController.php
  24. 1 1
      src/Controllers/AuthController.php
  25. 2 2
      src/Controllers/PasswordController.php
  26. 12 12
      src/Controllers/User/InfoController.php
  27. 3 0
      src/Controllers/User/MoneyController.php
  28. 10 3
      src/Controllers/UserController.php
  29. 12 0
      src/Models/Payback.php
  30. 0 34
      src/Services/ChatGPT.php
  31. 38 2
      src/Services/LLM.php
  32. 2 1
      src/Services/MFA.php
  33. 1 1
      src/Services/Mail/Base.php
  34. 13 20
      src/Services/Mail/Mailgun.php
  35. 1 1
      src/Services/Mail/NullMail.php
  36. 14 7
      src/Services/Mail/Postal.php
  37. 14 12
      src/Services/Mail/SendGrid.php
  38. 1 1
      src/Services/Mail/Ses.php
  39. 6 3
      src/Services/Mail/Smtp.php
  40. 2 2
      src/Services/RateLimit.php
  41. 19 1
      src/Utils/ResponseHelper.php
  42. 21 30
      src/Utils/Telegram.php
  43. 0 4
      src/Utils/Telegram/Callback.php
  44. 12 3
      src/Utils/Tools.php

+ 4 - 3
composer.json

@@ -3,6 +3,7 @@
         "php": "^8.1",
         "ext-bcmath": "*",
         "ext-curl": "*",
+        "ext-fileinfo": "*",
         "ext-json": "*",
         "ext-mysqli": "*",
         "ext-openssl": "*",
@@ -17,8 +18,8 @@
         "geoip2/geoip2": "~2.0",
         "guzzlehttp/guzzle": "^7.4",
         "guzzlehttp/psr7": "^2.4",
-        "illuminate/database": "^9.0",
-        "illuminate/pagination": "^9.0",
+        "illuminate/database": "^10.0",
+        "illuminate/pagination": "^10.0",
         "irazasyed/telegram-bot-sdk": "^3",
         "khanamiryan/qrcode-detector-decoder": "*",
         "league/omnipay": "^3.2.1",
@@ -27,7 +28,7 @@
         "openai-php/client": "^0.6.1",
         "ozdemir/datatables": "^2",
         "phpmailer/phpmailer": "^6",
-        "postal/postal": "^1.0",
+        "postal/postal": "^2",
         "ramsey/uuid": "^4",
         "sendgrid/sendgrid": "^8",
         "sentry/sdk": "^3.3",

File diff suppressed because it is too large
+ 317 - 365
composer.lock


+ 0 - 4
config/.config.example.php

@@ -95,11 +95,7 @@ $_ENV['detect_gfw_url']      = 'http://example.com:8080/tcping?ip={ip}&port={por
 #离线检测
 $_ENV['enable_detect_offline']           = true;
 
-//以下所有均为高级设置(一般用不上,不用改---------------------------------------------------------------------
-
 // 主站是否提供 WebAPI
-// - 为了安全性,推荐使用 WebAPI 模式对接节点并关闭公网数据库连接。
-// - 如果你全部节点使用数据库连接或者拥有独立的 WebAPI 站点或 gRPC API,则可设为 false。
 $_ENV['WebAPI']     = true;
 
 #杂项

+ 4 - 4
resources/views/tabler/admin/setting/captcha.tpl

@@ -58,7 +58,7 @@
                                     <label class="form-label col-3 col-form-label">注册验证码</label>
                                     <div class="col">
                                         <select id="enable_reg_captcha" class="col form-select" value="{$settings['enable_reg_captcha']}">
-                                            <option value="0" {if $settings['enable_reg_captcha'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_reg_captcha']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_reg_captcha']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -67,7 +67,7 @@
                                     <label class="form-label col-3 col-form-label">登录验证码</label>
                                     <div class="col">
                                         <select id="enable_login_captcha" class="col form-select" value="{$settings['enable_login_captcha']}">
-                                            <option value="0" {if $settings['enable_login_captcha'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_login_captcha']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_login_captcha']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -76,7 +76,7 @@
                                     <label class="form-label col-3 col-form-label">签到验证码</label>
                                     <div class="col">
                                         <select id="enable_checkin_captcha" class="col form-select" value="{$settings['enable_checkin_captcha']}">
-                                            <option value="0" {if $settings['enable_checkin_captcha'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_checkin_captcha']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_checkin_captcha']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -85,7 +85,7 @@
                                     <label class="form-label col-3 col-form-label">重置密码验证码</label>
                                     <div class="col">
                                         <select id="enable_reset_password_captcha" class="col form-select" value="{$settings['enable_reset_password_captcha']}">
-                                            <option value="0" {if $settings['enable_reset_password_captcha'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_reset_password_captcha']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_reset_password_captcha']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 6 - 6
resources/views/tabler/admin/setting/cron.tpl

@@ -71,7 +71,7 @@
                                     <div class="col">
                                         <select id="enable_daily_finance_mail" class="col form-select"
                                                 value="{$settings['enable_daily_finance_mail']}">
-                                            <option value="0" {if $settings['enable_daily_finance_mail'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_daily_finance_mail']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_daily_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -81,7 +81,7 @@
                                     <div class="col">
                                         <select id="enable_weekly_finance_mail" class="col form-select"
                                                 value="{$settings['enable_weekly_finance_mail']}">
-                                            <option value="0" {if $settings['enable_weekly_finance_mail'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_weekly_finance_mail']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_weekly_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -91,7 +91,7 @@
                                     <div class="col">
                                         <select id="enable_monthly_finance_mail" class="col form-select"
                                                 value="{$settings['enable_monthly_finance_mail']}">
-                                            <option value="0" {if $settings['enable_monthly_finance_mail'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_monthly_finance_mail']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_monthly_finance_mail']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -104,7 +104,7 @@
                                     <label class="form-label col-3 col-form-label">是否启用节点被墙检测</label>
                                     <div class="col">
                                         <select id="enable_detect_gfw" class="col form-select" value="{$settings['enable_detect_gfw']}">
-                                            <option value="0" {if $settings['enable_detect_gfw'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_detect_gfw']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_detect_gfw']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -113,7 +113,7 @@
                                     <label class="form-label col-3 col-form-label">是否启用审计封禁</label>
                                     <div class="col">
                                         <select id="enable_detect_ban" class="col form-select" value="{$settings['enable_detect_ban']}">
-                                            <option value="0" {if $settings['enable_detect_ban'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_detect_ban']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_detect_ban']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -127,7 +127,7 @@
                                     <div class="col">
                                         <select id="enable_detect_inactive_user" class="col form-select"
                                                 value="{$settings['enable_detect_inactive_user']}">
-                                            <option value="0" {if $settings['enable_detect_inactive_user'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_detect_inactive_user']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_detect_inactive_user']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 1 - 1
resources/views/tabler/admin/setting/email.tpl

@@ -162,8 +162,8 @@
                                     <label class="form-label col-3 col-form-label">是否使用 TLS/SSL</label>
                                     <div class="col">
                                     <select id="smtp_ssl" class="col form-select" value="{$settings['smtp_ssl']}">
+                                        <option value="0" {if ! $settings['smtp_ssl']}selected{/if}>关闭</option>
                                         <option value="1" {if $settings['smtp_ssl']}selected{/if}>开启</option>
-                                        <option value="0" {if $settings['smtp_ssl'] === false}selected{/if}>关闭</option>
                                     </select>
                                     </div>
                                 </div>

+ 5 - 5
resources/views/tabler/admin/setting/feature.tpl

@@ -43,7 +43,7 @@
                                     <label class="form-label col-3 col-form-label">显示节点流媒体解锁情况</label>
                                     <div class="col">
                                         <select id="display_media" class="col form-select" value="{$settings['display_media']}">
-                                            <option value="0" {if $settings['display_media'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['display_media']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['display_media']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -52,7 +52,7 @@
                                     <label class="form-label col-3 col-form-label">显示用户订阅记录</label>
                                     <div class="col">
                                         <select id="display_subscribe_log" class="col form-select" value="{$settings['display_subscribe_log']}">
-                                            <option value="0" {if $settings['display_subscribe_log'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['display_subscribe_log']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['display_subscribe_log']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -61,7 +61,7 @@
                                     <label class="form-label col-3 col-form-label">显示用户审计记录</label>
                                     <div class="col">
                                         <select id="display_detect_log" class="col form-select" value="{$settings['display_detect_log']}">
-                                            <option value="0" {if $settings['display_detect_log'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['display_detect_log']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['display_detect_log']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -70,7 +70,7 @@
                                     <label class="form-label col-3 col-form-label">显示文档</label>
                                     <div class="col">
                                         <select id="display_docs" class="col form-select" value="{$settings['display_docs']}">
-                                            <option value="0" {if $settings['display_docs'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['display_docs']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['display_docs']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -79,7 +79,7 @@
                                     <label class="form-label col-3 col-form-label">文档仅付费用户可见</label>
                                     <div class="col">
                                         <select id="display_docs_only_for_paid_user" class="col form-select" value="{$settings['display_docs_only_for_paid_user']}">
-                                            <option value="0" {if $settings['display_docs_only_for_paid_user'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['display_docs_only_for_paid_user']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['display_docs_only_for_paid_user']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 18 - 18
resources/views/tabler/admin/setting/im.tpl

@@ -46,7 +46,7 @@
                                     <label class="form-label col-3 col-form-label">添加节点通知</label>
                                     <div class="col">
                                         <select id="telegram_add_node" class="col form-select" value="{$settings['telegram_add_node']}">
-                                            <option value="0" {if $settings['telegram_add_node'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_add_node']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_add_node']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -61,7 +61,7 @@
                                     <label class="form-label col-3 col-form-label">修改节点通知</label>
                                     <div class="col">
                                         <select id="telegram_update_node" class="col form-select" value="{$settings['telegram_update_node']}">
-                                            <option value="0" {if $settings['telegram_update_node'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_update_node']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_update_node']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -76,7 +76,7 @@
                                     <label class="form-label col-3 col-form-label">删除节点通知</label>
                                     <div class="col">
                                         <select id="telegram_delete_node" class="col form-select" value="{$settings['telegram_delete_node']}">
-                                            <option value="0" {if $settings['telegram_delete_node'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_delete_node']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_delete_node']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -91,7 +91,7 @@
                                     <label class="form-label col-3 col-form-label">节点被墙通知</label>
                                     <div class="col">
                                         <select id="telegram_node_gfwed" class="col form-select" value="{$settings['telegram_node_gfwed']}">
-                                            <option value="0" {if $settings['telegram_node_gfwed'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_node_gfwed']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_node_gfwed']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -106,7 +106,7 @@
                                     <label class="form-label col-3 col-form-label">节点被墙恢复通知</label>
                                     <div class="col">
                                         <select id="telegram_node_ungfwed" class="col form-select" value="{$settings['telegram_node_ungfwed']}">
-                                            <option value="0" {if $settings['telegram_node_ungfwed'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_node_ungfwed']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_node_ungfwed']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -121,7 +121,7 @@
                                     <label class="form-label col-3 col-form-label">节点离线通知</label>
                                     <div class="col">
                                         <select id="telegram_node_offline" class="col form-select" value="{$settings['telegram_node_offline']}">
-                                            <option value="0" {if $settings['telegram_node_offline'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_node_offline']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_node_offline']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -136,7 +136,7 @@
                                     <label class="form-label col-3 col-form-label">节点上线通知</label>
                                     <div class="col">
                                         <select id="telegram_node_online" class="col form-select" value="{$settings['telegram_node_online']}">
-                                            <option value="0" {if $settings['telegram_node_online'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_node_online']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_node_online']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -151,7 +151,7 @@
                                     <label class="form-label col-3 col-form-label">每日任务通知</label>
                                     <div class="col">
                                         <select id="telegram_daily_job" class="col form-select" value="{$settings['telegram_daily_job']}">
-                                            <option value="0" {if $settings['telegram_daily_job'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_daily_job']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_daily_job']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -166,7 +166,7 @@
                                     <label class="form-label col-3 col-form-label">系统运行状况通知</label>
                                     <div class="col">
                                         <select id="telegram_diary" class="col form-select" value="{$settings['telegram_diary']}">
-                                            <option value="0" {if $settings['telegram_diary'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_diary']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_diary']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -185,7 +185,7 @@
                                     <label class="form-label col-3 col-form-label">解绑 Telegram 账户后自动踢出群组</label>
                                     <div class="col">
                                         <select id="telegram_unbind_kick_member" class="col form-select" value="{$settings['telegram_unbind_kick_member']}">
-                                            <option value="0" {if $settings['telegram_unbind_kick_member'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_unbind_kick_member']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_unbind_kick_member']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -194,7 +194,7 @@
                                     <label class="form-label col-3 col-form-label">仅允许已绑定 Telegram 账户的用户加入群组</label>
                                     <div class="col">
                                         <select id="telegram_group_bound_user" class="col form-select" value="{$settings['telegram_group_bound_user']}">
-                                            <option value="0" {if $settings['telegram_group_bound_user'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_group_bound_user']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_group_bound_user']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -203,7 +203,7 @@
                                     <label class="form-label col-3 col-form-label">启用 Telegram 机器人显示用户群组链接</label>
                                     <div class="col">
                                         <select id="telegram_show_group_link" class="col form-select" value="{$settings['telegram_show_group_link']}">
-                                            <option value="0" {if $settings['telegram_show_group_link'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_show_group_link']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_show_group_link']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -218,7 +218,7 @@
                                     <label class="form-label col-3 col-form-label">Telegram 机器人发送欢迎消息</label>
                                     <div class="col">
                                         <select id="enable_welcome_message" class="col form-select" value="{$settings['enable_welcome_message']}">
-                                            <option value="0" {if $settings['enable_welcome_message'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_welcome_message']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_welcome_message']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -227,7 +227,7 @@
                                     <label class="form-label col-3 col-form-label">Telegram 机器人在群组中不回应</label>
                                     <div class="col">
                                         <select id="telegram_group_quiet" class="col form-select" value="{$settings['telegram_group_quiet']}">
-                                            <option value="0" {if $settings['telegram_group_quiet'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['telegram_group_quiet']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['telegram_group_quiet']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -236,7 +236,7 @@
                                     <label class="form-label col-3 col-form-label">允许 Bot 加入下方配置之外的群组</label>
                                     <div class="col">
                                         <select id="allow_to_join_new_groups" class="col form-select" value="{$settings['allow_to_join_new_groups']}">
-                                            <option value="0" {if $settings['allow_to_join_new_groups'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['allow_to_join_new_groups']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['allow_to_join_new_groups']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -257,7 +257,7 @@
                                     <label class="form-label col-3 col-form-label">非管理员操作管理员功能是否回复</label>
                                     <div class="col">
                                         <select id="enable_not_admin_reply" class="col form-select" value="{$settings['enable_not_admin_reply']}">
-                                            <option value="0" {if $settings['enable_not_admin_reply'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_not_admin_reply']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_not_admin_reply']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -284,7 +284,7 @@
                                     <label class="form-label col-3 col-form-label">允许任意未知的命令触发 /help 的回复</label>
                                     <div class="col">
                                         <select id="help_any_command" class="col form-select" value="{$settings['help_any_command']}">
-                                            <option value="0" {if $settings['help_any_command'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['help_any_command']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['help_any_command']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -293,7 +293,7 @@
                                     <label class="form-label col-3 col-form-label">开启在群组搜寻用户信息时显示用户完整邮箱,关闭则会对邮箱中间内容打码</label>
                                     <div class="col">
                                         <select id="enable_user_email_group_show" class="col form-select" value="{$settings['enable_user_email_group_show']}">
-                                            <option value="0" {if $settings['enable_user_email_group_show'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_user_email_group_show']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_user_email_group_show']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 3 - 3
resources/views/tabler/admin/setting/reg.tpl

@@ -59,7 +59,7 @@
                                     <label class="form-label col-3 col-form-label">邮箱验证</label>
                                     <div class="col">
                                         <select id="reg_email_verify" class="col form-select" value="{$settings['reg_email_verify']}">
-                                            <option value="0" {if $settings['reg_email_verify'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['reg_email_verify']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['reg_email_verify']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -68,7 +68,7 @@
                                     <label class="form-label col-3 col-form-label">默认接收每日用量邮件推送</label>
                                     <div class="col">
                                         <select id="sign_up_for_daily_report" class="col form-select" value="{$settings['sign_up_for_daily_report']}">
-                                            <option value="0" {if $settings['sign_up_for_daily_report'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['sign_up_for_daily_report']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['sign_up_for_daily_report']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -77,7 +77,7 @@
                                     <label class="form-label col-3 col-form-label">是否要求用户输入IM联系方式</label>
                                     <div class="col">
                                         <select id="enable_reg_im" class="col form-select" value="{$settings['enable_reg_im']}">
-                                            <option value="0" {if $settings['enable_reg_im'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_reg_im']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_reg_im']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 5 - 5
resources/views/tabler/admin/setting/sub.tpl

@@ -43,7 +43,7 @@
                                     <label class="form-label col-3 col-form-label">启用传统订阅系统</label>
                                     <div class="col">
                                         <select id="enable_traditional_sub" class="col form-select" value="{$settings['enable_traditional_sub']}">
-                                            <option value="0" {if $settings['enable_traditional_sub'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_traditional_sub']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_traditional_sub']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -52,7 +52,7 @@
                                     <label class="form-label col-3 col-form-label">启用 Shadowsocks 订阅(仅影响前端显示与传统/sip002/sip008订阅)</label>
                                     <div class="col">
                                         <select id="enable_ss_sub" class="col form-select" value="{$settings['enable_ss_sub']}">
-                                            <option value="0" {if $settings['enable_ss_sub'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_ss_sub']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_ss_sub']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -61,7 +61,7 @@
                                     <label class="form-label col-3 col-form-label">启用 V2Ray 订阅(仅影响前端显示与传统订阅)</label>
                                     <div class="col">
                                         <select id="enable_v2_sub" class="col form-select" value="{$settings['enable_v2_sub']}">
-                                            <option value="0" {if $settings['enable_v2_sub'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_v2_sub']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_v2_sub']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -70,7 +70,7 @@
                                     <label class="form-label col-3 col-form-label">启用 Trojan 订阅(仅影响前端显示与传统订阅)</label>
                                     <div class="col">
                                         <select id="enable_trojan_sub" class="col form-select" value="{$settings['enable_trojan_sub']}">
-                                            <option value="0" {if $settings['enable_trojan_sub'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_trojan_sub']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_trojan_sub']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -79,7 +79,7 @@
                                     <label class="form-label col-3 col-form-label">用户修改账户登录密码时,是否强制更换订阅地址</label>
                                     <div class="col">
                                         <select id="enable_forced_replacement" class="col form-select" value="{$settings['enable_forced_replacement']}">
-                                            <option value="0" {if $settings['enable_forced_replacement'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_forced_replacement']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_forced_replacement']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 2 - 2
resources/views/tabler/admin/setting/support.tpl

@@ -86,7 +86,7 @@
                                     <label class="form-label col-3 col-form-label">启用工单系统</label>
                                     <div class="col">
                                         <select id="enable_ticket" class="col form-select" value="{$settings['enable_ticket']}">
-                                            <option value="0" {if $settings['enable_ticket'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['enable_ticket']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['enable_ticket']}selected{/if}>开启</option>
                                         </select>
                                     </div>
@@ -95,7 +95,7 @@
                                     <label class="form-label col-3 col-form-label">启用工单邮件提醒</label>
                                     <div class="col">
                                         <select id="mail_ticket" class="col form-select" value="{$settings['mail_ticket']}">
-                                            <option value="0" {if $settings['mail_ticket'] === false}selected{/if}>关闭</option>
+                                            <option value="0" {if ! $settings['mail_ticket']}selected{/if}>关闭</option>
                                             <option value="1" {if $settings['mail_ticket']}selected{/if}>开启</option>
                                         </select>
                                     </div>

+ 1 - 1
resources/views/tabler/user/detect/index.tpl

@@ -1,6 +1,6 @@
 {include file='user/header.tpl'}
 
-<!-- 审计规则是用来防止DMCA和Spam,不是用来给用户建墙用的,不要以为把“违法网站”墙了,被抓了能少判哪怕一天的刑期 -->
+<!-- 审计规则是用来防止 DMCA 邮件 Spam,不是用来给用户建墙用的,不要以为你在中国开机场同时把“违法网站”墙了,被抓了能少判哪怕一天的刑期 -->
 <div class="page-wrapper">
     <div class="container-xl">
         <div class="page-header d-print-none text-white">

+ 1 - 1
resources/views/tabler/user/edit.tpl

@@ -61,7 +61,7 @@
                                                     <p>当前邮箱:<code>{$user->email}</code></p>
                                                     <div class="mb-3">
                                                         <input id="new-email" type="email" class="form-control"
-                                                            placeholder="新邮箱" {if $config['enable_change_email'] === false}disabled=""{/if}>
+                                                            placeholder="新邮箱" {if ! $config['enable_change_email']}disabled=""{/if}>
                                                     </div>
                                                     {if $public_setting['reg_email_verify'] && $config['enable_change_email']}
                                                     <div class="mb-3">

+ 3 - 4
resources/views/tabler/user/index.tpl

@@ -448,11 +448,10 @@
                                 </div>
                             </div>
                             <p class="my-3">
-                                {if time() > strtotime($user->class_expire)}
-                                    你的套餐过期了,可以前往 <a href="/user/product">商店</a> 购买套餐
+                                {if $user->class === 0}
+                                    前往 <a href="/user/product">商店</a> 购买套餐
                                 {else}
-                                    {$diff = round((strtotime($user->class_expire) - time()) / 86400)}
-                                    你的 LV. {$user->class} 套餐大约还有 {$diff} 天到期({$user->class_expire})
+                                    你的 LV. {$user->class} 账户会在 {$class_expire_days} 天后到期({$user->class_expire})
                                 {/if}
                             </p>
                         </div>

+ 3 - 7
resources/views/tabler/user/invite.tpl

@@ -25,7 +25,7 @@
                                 <div class="card-body">
                                     <h3 class="card-title">邀请规则</h3>
                                     <ul>
-                                        <li>邀请注册的用户在账单确认后,你可获得其账单金额的 <code>{$public_setting['rebate_ratio'] * 100} %</code>
+                                        <li>邀请注册的用户在账单确认后,你可获得其账单金额的 <code>{$rebate_ratio_per} %</code>
                                             作为返利</li>
                                         <li>具体邀请返利规则请查看公告,或通过工单系统询问管理员</li>
                                         <li>部分商品的返利比例可能不遵循上面的比例</li>
@@ -58,7 +58,7 @@
                         <div class="card-header">
                             <h3 class="card-title">返利记录</h3>
                         </div>
-                        {if $paybacks->count() !== 0}
+                        {if $payback_count !== 0}
                             <div class="table-responsive">
                                 <table class="table card-table table-vcenter text-nowrap datatable">
                                     <thead>
@@ -75,11 +75,7 @@
                                             <tr>
                                                 <td>{$payback->id}</td>
                                                 <td>{$payback->userid}</td>
-                                                {if $payback->user()!=null}
-                                                    <td>{$payback->user()->user_name}</td>
-                                                {else}
-                                                    <td>已注销</td>
-                                                {/if}
+                                                <td>{$payback->user_name}</td>
                                                 <td>{$payback->ref_get} 元</td>
                                                 <td>{$payback->datetime}</td>
                                             </tr>

+ 1 - 1
resources/views/tabler/user/money.tpl

@@ -32,7 +32,7 @@
                         <div class="card-header">
                             <h3 class="card-title">账户余额记录</h3>
                         </div>
-                        {if $moneylogs->count() !== 0}
+                        {if $moneylog_count !== 0}
                             <div class="table-responsive">
                                 <table class="table card-table table-vcenter text-nowrap datatable">
                                     <thead>

+ 0 - 8
resources/views/tabler/user/server.tpl

@@ -122,13 +122,5 @@
             </div>
         </div>
     </div>
-
-    <script>
-        var clipboard = new ClipboardJS(".ti-copy");
-        clipboard.on("success", function(e) {
-            $("#success-message").text("已复制到剪切板");
-            $("#success-dialog").modal("show");
-        });
-    </script>
     
 {include file="user/footer.tpl"}

+ 14 - 17
src/Command/Tool.php

@@ -16,10 +16,21 @@ use Telegram\Bot\Api;
 use Telegram\Bot\Exceptions\TelegramSDKException;
 use Vectorface\GoogleAuthenticator;
 use function count;
+use function date;
+use function fgets;
+use function file_get_contents;
+use function file_put_contents;
+use function fwrite;
 use function in_array;
 use function json_decode;
 use function json_encode;
-use function time;
+use function method_exists;
+use function strtolower;
+use function trim;
+use const JSON_PRETTY_PRINT;
+use const JSON_UNESCAPED_UNICODE;
+use const PHP_EOL;
+use const STDIN;
 
 final class Tool extends Command
 {
@@ -30,7 +41,6 @@ final class Tool extends Command
 │ ├─ exportAllSettings       - 导出所有设置
 │ ├─ importAllSettings       - 导入所有设置
 │ ├─ resetNodePassword       - 重置所有节点通讯密钥
-│ ├─ getCookie               - 获取指定用户的 Cookie
 │ ├─ resetPort               - 重置单个用户端口
 │ ├─ createAdmin             - 创建管理员帐号
 │ ├─ resetAllPort            - 重置所有用户端口
@@ -255,12 +265,11 @@ EOL;
 
             try {
                 $secret = $ga->createSecret();
+                $user->ga_token = $secret;
+                $user->save();
             } catch (Exception $e) {
                 echo $e->getMessage();
             }
-
-            $user->ga_token = $secret;
-            $user->save();
         }
 
         echo 'generate Ga Secret successful';
@@ -355,18 +364,6 @@ EOL;
         }
     }
 
-    /**
-     * 获取 USERID 的 Cookie
-     */
-    public function getCookie(): void
-    {
-        if (count($this->argv) === 4) {
-            $user = ModelsUser::find($this->argv[3]);
-            $expire_in = 86400 + time();
-            echo Hash::cookieHash($user->pass, $expire_in) . ' ' . $expire_in;
-        }
-    }
-
     /**
      * 为所有用户设置新的主题
      */

+ 2 - 15
src/Controllers/Admin/DocsController.php

@@ -6,8 +6,7 @@ namespace App\Controllers\Admin;
 
 use App\Controllers\BaseController;
 use App\Models\Docs;
-use App\Services\ChatGPT;
-use App\Services\PaLM;
+use App\Services\LLM;
 use App\Utils\Tools;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
@@ -92,19 +91,7 @@ final class DocsController extends BaseController
      */
     public function generate(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
-        $question = $request->getParam('question');
-
-        // 这里可能要等4-5秒
-        if ($_ENV['llm_backend'] === 'openai') {
-            $content = ChatGPT::askOnce($question);
-        } elseif ($_ENV['llm_backend'] === 'palm') {
-            $content = PaLM::textPrompt($question);
-        } else {
-            return $response->withJson([
-                'ret' => 0,
-                'msg' => 'LLM 后端配置错误',
-            ]);
-        }
+        $content = LLM::genTextResponse($request->getParam('question'));
 
         return $response->withJson([
             'ret' => 1,

+ 2 - 2
src/Controllers/Admin/InviteController.php

@@ -152,8 +152,8 @@ final class InviteController extends BaseController
 
         foreach ($paybacks as $payback) {
             $payback->datetime = Tools::toDateTime((int) $payback->datetime);
-            $payback->user_name = $payback->user() === null ? '已注销' : $payback->user()->user_name;
-            $payback->ref_user_name = $payback->refUser() === null ? '已注销' : $payback->refUser()->user_name;
+            $payback->user_name = $payback->getAttributes();
+            $payback->ref_user_name = $payback->getAttributes();
         }
 
         return $response->withJson([

+ 1 - 4
src/Controllers/Admin/IpController.php

@@ -9,7 +9,6 @@ use App\Models\LoginIp;
 use App\Services\DB;
 use App\Utils\Tools;
 use Exception;
-use GeoIp2\Exception\AddressNotFoundException;
 use MaxMind\Db\Reader\InvalidDatabaseException;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
@@ -66,7 +65,6 @@ final class IpController extends BaseController
     /**
      * 后台登录记录页面 AJAX
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
     public function ajaxLogin(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
@@ -110,7 +108,6 @@ final class IpController extends BaseController
     /**
      * 后台在线 IP 页面 AJAX
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
     public function ajaxOnline(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
@@ -148,7 +145,7 @@ final class IpController extends BaseController
                     'node_id' => $val->node_id,
                     'node_name' => $val->node_name,
                     'ip' => str_replace('::ffff:', '', $val->ip),
-                    'location' => Tools::getIpLocation($val->ip),
+                    'location' => Tools::getIpLocation(str_replace('::ffff:', '', $val->ip)),
                     'first_time' => Tools::toDateTime($val->first_time),
                     'last_time' => Tools::toDateTime($val->last_time),
                 ];

+ 0 - 2
src/Controllers/Admin/SubscribeLogController.php

@@ -8,7 +8,6 @@ use App\Controllers\BaseController;
 use App\Models\SubscribeLog;
 use App\Utils\Tools;
 use Exception;
-use GeoIp2\Exception\AddressNotFoundException;
 use MaxMind\Db\Reader\InvalidDatabaseException;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
@@ -46,7 +45,6 @@ final class SubscribeLogController extends BaseController
     /**
      * 后台订阅记录页面 AJAX
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
     public function ajax(ServerRequest $request, Response $response, array $args): ResponseInterface

+ 2 - 14
src/Controllers/Admin/TicketController.php

@@ -7,8 +7,7 @@ namespace App\Controllers\Admin;
 use App\Controllers\BaseController;
 use App\Models\Ticket;
 use App\Models\User;
-use App\Services\ChatGPT;
-use App\Services\PaLM;
+use App\Services\LLM;
 use App\Utils\Tools;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
@@ -121,18 +120,7 @@ final class TicketController extends BaseController
 
         $content_old = json_decode($ticket->content, true);
         // 获取用户的第一个问题,作为 LLM 的输入
-        $user_question = $content_old[0]['comment'];
-        // 这里可能要等4-5秒
-        if ($_ENV['llm_backend'] === 'openai') {
-            $ai_reply = ChatGPT::askOnce($user_question);
-        } elseif ($_ENV['llm_backend'] === 'palm') {
-            $ai_reply = PaLM::textPrompt($user_question);
-        } else {
-            return $response->withJson([
-                'ret' => 0,
-                'msg' => 'LLM 后端配置错误',
-            ]);
-        }
+        $ai_reply = LLM::genTextResponse($content_old[0]['comment']);
 
         $content_new = [
             [

+ 1 - 1
src/Controllers/AuthController.php

@@ -207,7 +207,7 @@ final class AuthController extends BaseController
                 return ResponseHelper::error($response, '邮件发送失败,请联系网站管理员。');
             }
 
-            return ResponseHelper::successfully($response, '验证码发送成功,请查收邮件。');
+            return ResponseHelper::success($response, '验证码发送成功,请查收邮件。');
         }
 
         return ResponseHelper::error($response, '站点未启用邮件验证');

+ 2 - 2
src/Controllers/PasswordController.php

@@ -83,7 +83,7 @@ final class PasswordController extends BaseController
             }
         }
 
-        return ResponseHelper::successfully($response, $msg);
+        return ResponseHelper::success($response, $msg);
     }
 
     /**
@@ -150,6 +150,6 @@ final class PasswordController extends BaseController
 
         $redis->del($token);
 
-        return ResponseHelper::successfully($response, '重置成功');
+        return ResponseHelper::success($response, '重置成功');
     }
 }

+ 12 - 12
src/Controllers/User/InfoController.php

@@ -103,7 +103,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updateUsername(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -122,7 +122,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updatePassword(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -159,7 +159,7 @@ final class InfoController extends BaseController
             $user->cleanLink();
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updateContact(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
@@ -188,7 +188,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updateTheme(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
@@ -207,7 +207,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updateDailyMail(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -233,7 +233,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function resetPasswd(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -246,7 +246,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function resetApiToken(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -258,7 +258,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function updateMethod(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -281,7 +281,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '修改失败');
         }
 
-        return ResponseHelper::successfully($response, '修改成功');
+        return ResponseHelper::success($response, '修改成功');
     }
 
     public function resetURL(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -289,7 +289,7 @@ final class InfoController extends BaseController
         $user = $this->user;
         $user->cleanLink();
 
-        return ResponseHelper::successfully($response, '重置成功');
+        return ResponseHelper::success($response, '重置成功');
     }
 
     public function resetInviteURL(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -297,7 +297,7 @@ final class InfoController extends BaseController
         $user = $this->user;
         $user->clearInviteCodes();
 
-        return ResponseHelper::successfully($response, '重置成功');
+        return ResponseHelper::success($response, '重置成功');
     }
 
     public function sendToGulag(ServerRequest $request, Response $response, array $args): ResponseInterface
@@ -316,7 +316,7 @@ final class InfoController extends BaseController
         if ($_ENV['enable_kill']) {
             Auth::logout();
             $user->killUser();
-            return ResponseHelper::successfully($response, '你的帐号已被送去古拉格劳动改造,再见');
+            return ResponseHelper::success($response, '你的帐号已被送去古拉格劳动改造,再见');
         }
 
         return ResponseHelper::error($response, '自助账号删除未启用');

+ 3 - 0
src/Controllers/User/MoneyController.php

@@ -32,9 +32,12 @@ final class MoneyController extends BaseController
             $moneylog->create_time = Tools::toDateTime($moneylog->create_time);
         }
 
+        $moneylog_count = $moneylogs->count();
+
         return $response->write(
             $this->view()
                 ->assign('moneylogs', $moneylogs)
+                ->assign('moneylog_count', $moneylog_count)
                 ->fetch('user/money.tpl')
         );
     }

+ 10 - 3
src/Controllers/UserController.php

@@ -34,6 +34,8 @@ final class UserController extends BaseController
     public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         $captcha = [];
+        $class_expire_days = $this->user->class > 0 ?
+            round((strtotime($this->user->class_expire) - time()) / 86400) : 0;
 
         if (Setting::obtain('enable_checkin_captcha')) {
             $captcha = Captcha::generate();
@@ -42,9 +44,10 @@ final class UserController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('ann', Ann::orderBy('date', 'desc')->first())
+                ->assign('captcha', $captcha)
+                ->assign('class_expire_days', $class_expire_days)
                 ->assign('UniversalSub', SubController::getUniversalSub($this->user))
                 ->assign('TraditionalSub', LinkController::getTraditionalSub($this->user))
-                ->assign('captcha', $captcha)
                 ->fetch('user/index.tpl')
         );
     }
@@ -121,12 +124,16 @@ final class UserController extends BaseController
         }
 
         $invite_url = $_ENV['baseUrl'] . '/auth/register?code=' . $code->code;
+        $rebate_ratio_per = Setting::obtain('rebate_ratio') * 100;
+        $payback_count = $paybacks->count();
 
         return $response->write($this->view()
             ->assign('code', $code)
             ->assign('paybacks', $paybacks)
             ->assign('invite_url', $invite_url)
             ->assign('paybacks_sum', $paybacks_sum)
+            ->assign('rebate_ratio_per', $rebate_ratio_per)
+            ->assign('payback_count', $payback_count)
             ->fetch('user/invite.tpl'));
     }
 
@@ -189,7 +196,7 @@ final class UserController extends BaseController
         $user = $this->user;
         $user->telegramReset();
 
-        return ResponseHelper::successfully($response, '重置成功');
+        return ResponseHelper::success($response, '重置成功');
     }
 
     public function switchThemeMode(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
@@ -201,6 +208,6 @@ final class UserController extends BaseController
             return ResponseHelper::error($response, '切换失败');
         }
 
-        return ResponseHelper::successfully($response, '切换成功');
+        return ResponseHelper::success($response, '切换成功');
     }
 }

+ 12 - 0
src/Models/Payback.php

@@ -16,11 +16,23 @@ final class Payback extends Model
         return User::where('id', $this->userid)->first();
     }
 
+    public function getUserNameAttribute(): string
+    {
+        return User::where('id', $this->userid)->first() === null ? '已注销' :
+            User::where('id', $this->userid)->first()->user_name;
+    }
+
     public function refUser()
     {
         return User::where('id', $this->ref_by)->first();
     }
 
+    public function getRefUserNameAttribute(): string
+    {
+        return User::where('id', $this->ref_by)->first() === null ? '已注销' :
+            User::where('id', $this->ref_by)->first()->user_name;
+    }
+
     public static function rebate($user_id, $order_amount): void
     {
         $configs = Setting::getClass('invite');

+ 0 - 34
src/Services/ChatGPT.php

@@ -1,34 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace App\Services;
-
-use OpenAI;
-
-final class ChatGPT
-{
-    public static function askOnce(string $q): string
-    {
-        if ($_ENV['openai_api_key'] === '') {
-            return 'OpenAI API key not set';
-        }
-
-        if ($q === '') {
-            return 'No question provided';
-        }
-
-        $client = OpenAI::client($_ENV['openai_api_key']);
-        $response = $client->chat()->create([
-            'model' => $_ENV['openai_model'],
-            'messages' => [
-                [
-                    'role' => 'user',
-                    'content' => $q,
-                ],
-            ],
-        ]);
-
-        return $response->choices[0]->message->content;
-    }
-}

+ 38 - 2
src/Services/PaLM.php → src/Services/LLM.php

@@ -4,12 +4,48 @@ declare(strict_types=1);
 
 namespace App\Services;
 
+use OpenAI;
+use function file_get_contents;
 use function json_decode;
 use function json_encode;
+use function stream_context_create;
 
-final class PaLM
+final class LLM
 {
-    public static function textPrompt(string $q): string
+    public static function genTextResponse(string $q): string
+    {
+        return match ($_ENV['llm_backend']) {
+            'openai' => self::textPromptGPT($q),
+            'palm' => self::textPromptPaLM($q),
+            default => 'No LLM backend configured',
+        };
+    }
+
+    public static function textPromptGPT(string $q): string
+    {
+        if ($_ENV['openai_api_key'] === '') {
+            return 'OpenAI API key not set';
+        }
+
+        if ($q === '') {
+            return 'No question provided';
+        }
+
+        $client = OpenAI::client($_ENV['openai_api_key']);
+        $response = $client->chat()->create([
+            'model' => $_ENV['openai_model'],
+            'messages' => [
+                [
+                    'role' => 'user',
+                    'content' => $q,
+                ],
+            ],
+        ]);
+
+        return $response->choices[0]->message->content;
+    }
+
+    public static function textPromptPaLM(string $q): string
     {
         if ($_ENV['palm_api_key'] === '') {
             return 'PaLM API key not set';

+ 2 - 1
src/Services/MFA.php

@@ -8,6 +8,7 @@ final class MFA
 {
     public static function getGAurl($user): string
     {
-        return 'otpauth://totp/' . rawurlencode($_ENV['appName'] . ' (' . $user->email . ')') . '?secret=' . $user->ga_token;
+        return 'otpauth://totp/' .
+            rawurlencode($_ENV['appName'] . ' (' . $user->email . ')') . '?secret=' . $user->ga_token;
     }
 }

+ 1 - 1
src/Services/Mail/Base.php

@@ -6,5 +6,5 @@ namespace App\Services\Mail;
 
 abstract class Base
 {
-    abstract public function send($to, $subject, $text, $file): void;
+    abstract public function send($to, $subject, $text, $files): void;
 }

+ 13 - 20
src/Services/Mail/Mailgun.php

@@ -8,7 +8,7 @@ use App\Models\Setting;
 use Exception;
 use Mailgun\Mailgun as MailgunService;
 use Psr\Http\Client\ClientExceptionInterface;
-use function count;
+use function basename;
 
 final class Mailgun extends Base
 {
@@ -41,29 +41,22 @@ final class Mailgun extends Base
      * @throws Exception
      * @throws ClientExceptionInterface
      */
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         $inline = [];
 
-        foreach ($file as $file_raw) {
-            $inline[] = ['filePath' => $file_raw, 'filename' => basename($file_raw)];
+        if ($files !== []) {
+            foreach ($files as $file_raw) {
+                $inline[] = ['filePath' => $file_raw, 'filename' => basename($file_raw)];
+            }
         }
 
-        if (count($inline) === 0) {
-            $this->mg->messages()->send($this->domain, [
-                'from' => $this->sender,
-                'to' => $to,
-                'subject' => $subject,
-                'html' => $text,
-            ]);
-        } else {
-            $this->mg->messages()->send($this->domain, [
-                'from' => $this->sender,
-                'to' => $to,
-                'subject' => $subject,
-                'html' => $text,
-                'inline' => $inline,
-            ]);
-        }
+        $this->mg->messages()->send($this->domain, [
+            'from' => $this->sender,
+            'to' => $to,
+            'subject' => $subject,
+            'html' => $text,
+            'inline' => $inline,
+        ]);
     }
 }

+ 1 - 1
src/Services/Mail/NullMail.php

@@ -16,7 +16,7 @@ final class NullMail extends Base
         ];
     }
 
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         echo '';
     }

+ 14 - 7
src/Services/Mail/Postal.php

@@ -6,19 +6,21 @@ namespace App\Services\Mail;
 
 use App\Models\Setting;
 use Postal\Client;
-use Postal\SendMessage;
+use Postal\Send\Message;
+use function basename;
+use function mime_content_type;
 
 final class Postal extends Base
 {
     private array $config;
     private Client $client;
-    private SendMessage $message;
+    private Message $message;
 
     public function __construct()
     {
         $this->config = $this->getConfig();
         $this->client = new Client($this->config['host'], $this->config['key']);
-        $this->message = new SendMessage($this->client);
+        $this->message = new Message();
         $this->message->sender($this->config['sender']); # 发件邮箱
         $this->message->from($this->config['name']. ' <' . $this->config['sender'] . '>'); # 发件人
         $this->message->replyTo($this->config['sender']);
@@ -27,6 +29,7 @@ final class Postal extends Base
     public function getConfig(): array
     {
         $configs = Setting::getClass('postal');
+
         return [
             'host' => $configs['postal_host'],
             'key' => $configs['postal_key'],
@@ -35,15 +38,19 @@ final class Postal extends Base
         ];
     }
 
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         $this->message->subject($subject);
         $this->message->to($to);
         $this->message->plainBody($text);
         $this->message->htmlBody($text);
-        foreach ($file as $file_raw) {
-            $this->message->attach(basename($file_raw), 'text/plain', $file_raw);
+
+        if ($files !== []) {
+            foreach ($files as $file_raw) {
+                $this->message->attach(basename($file_raw), mime_content_type($file_raw), $file_raw);
+            }
         }
-        $this->message->send();
+
+        $this->client->send->message($this->message);
     }
 }

+ 14 - 12
src/Services/Mail/SendGrid.php

@@ -7,6 +7,9 @@ namespace App\Services\Mail;
 use App\Models\Setting;
 use SendGrid\Mail\Mail;
 use SendGrid\Mail\TypeException;
+use function base64_encode;
+use function basename;
+use function file_get_contents;
 
 final class SendGrid extends Base
 {
@@ -39,24 +42,23 @@ final class SendGrid extends Base
     /**
      * @throws TypeException
      */
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         $this->email->setFrom($this->sender, $this->name);
-
         $this->email->setSubject($subject);
-
         $this->email->addTo($to);
-
         $this->email->addContent('text/html', $text);
 
-        foreach ($file as $file_raw) {
-            $this->email->addAttachment(
-                base64_encode(file_get_contents($file_raw)),
-                'application/octet-stream',
-                basename($file_raw),
-                'attachment',
-                'attachment'
-            );
+        if ($files !== []) {
+            foreach ($files as $file_raw) {
+                $this->email->addAttachment(
+                    base64_encode(file_get_contents($file_raw)),
+                    'application/octet-stream',
+                    basename($file_raw),
+                    'attachment',
+                    'attachment'
+                );
+            }
         }
 
         $response = $this->sg->send($this->email);

+ 1 - 1
src/Services/Mail/Ses.php

@@ -27,7 +27,7 @@ final class Ses extends Base
         $this->ses = $ses;
     }
 
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         $ses = $this->ses;
         $configs = Setting::getClass('aws_ses');

+ 6 - 3
src/Services/Mail/Smtp.php

@@ -46,15 +46,18 @@ final class Smtp extends Base
      * @throws \PHPMailer\PHPMailer\Exception
      * @throws Exception
      */
-    public function send($to, $subject, $text, $file): void
+    public function send($to, $subject, $text, $files): void
     {
         $mail = $this->mail;
         $mail->addAddress($to);     // Add a recipient
         $mail->isHTML();
         $mail->Subject = $subject;
         $mail->Body = $text;
-        foreach ($file as $file_raw) {
-            $mail->addAttachment($file_raw);
+
+        if ($files !== []) {
+            foreach ($files as $file_raw) {
+                $mail->addAttachment($file_raw);
+            }
         }
 
         if (! $mail->send()) {

+ 2 - 2
src/Services/RateLimit.php

@@ -148,7 +148,7 @@ final class RateLimit
     /**
      * @throws RedisException
      */
-    public static function checkTicketLimit(string $user_id): bool
+    public static function checkTicketLimit(int $user_id): bool
     {
         $ticket_limiter = new RedisRateLimiter(
             Rate::custom(Setting::obtain('ticket_limit'), 2592000),
@@ -156,7 +156,7 @@ final class RateLimit
         );
 
         try {
-            $ticket_limiter->limit($user_id);
+            $ticket_limiter->limit((string) $user_id);
         } catch (LimitExceeded $e) {
             return false;
         }

+ 19 - 1
src/Utils/ResponseHelper.php

@@ -12,7 +12,7 @@ use function json_encode;
 
 final class ResponseHelper
 {
-    public static function successfully(Response $response, string $msg): ResponseInterface
+    public static function success(Response $response, string $msg): ResponseInterface
     {
         return $response->withJson([
             'ret' => 1,
@@ -20,6 +20,15 @@ final class ResponseHelper
         ]);
     }
 
+    public static function successWithData(Response $response, string $msg, array $data): ResponseInterface
+    {
+        return $response->withJson([
+            'ret' => 1,
+            'msg' => $msg,
+            'data' => $data,
+        ]);
+    }
+
     public static function error(Response $response, string $msg): ResponseInterface
     {
         return $response->withJson([
@@ -28,6 +37,15 @@ final class ResponseHelper
         ]);
     }
 
+    public static function errorWithData(Response $response, string $msg, array $data): ResponseInterface
+    {
+        return $response->withJson([
+            'ret' => 0,
+            'msg' => $msg,
+            'data' => $data,
+        ]);
+    }
+
     /**
      * Build a JSON response with ETag header.
      *

+ 21 - 30
src/Utils/Telegram.php

@@ -21,13 +21,12 @@ final class Telegram
      */
     public static function send(string $messageText, int $chat_id = 0): void
     {
-        $bot = null;
-
-        if ($chat_id === 0) {
-            $chat_id = $_ENV['telegram_chatid'];
-        }
-
         if ($_ENV['enable_telegram']) {
+            $bot = null;
+
+            if ($chat_id === 0) {
+                $chat_id = $_ENV['telegram_chatid'];
+            }
             // 发送给非群组时使用异步
             $async = ($chat_id !== $_ENV['telegram_chatid']);
             $bot = new Api($_ENV['telegram_token'], $async);
@@ -51,13 +50,12 @@ final class Telegram
      */
     public static function sendHtml(string $messageText, int $chat_id = 0): void
     {
-        $bot = null;
-
-        if ($chat_id === 0) {
-            $chat_id = $_ENV['telegram_chatid'];
-        }
-
         if ($_ENV['enable_telegram']) {
+            $bot = null;
+
+            if ($chat_id === 0) {
+                $chat_id = $_ENV['telegram_chatid'];
+            }
             // 发送给非群组时使用异步
             $async = ($chat_id !== $_ENV['telegram_chatid']);
             $bot = new Api($_ENV['telegram_token'], $async);
@@ -90,13 +88,12 @@ final class Telegram
      */
     public static function sendMarkdown(string $messageText, int $chat_id = 0): void
     {
-        $bot = null;
-
-        if ($chat_id === 0) {
-            $chat_id = $_ENV['telegram_chatid'];
-        }
-
         if ($_ENV['enable_telegram']) {
+            $bot = null;
+
+            if ($chat_id === 0) {
+                $chat_id = $_ENV['telegram_chatid'];
+            }
             // 发送给非群组时使用异步
             $async = ($chat_id !== $_ENV['telegram_chatid']);
             $bot = new Api($_ENV['telegram_token'], $async);
@@ -124,13 +121,12 @@ final class Telegram
      */
     public static function sendMarkdownV2(string $messageText, int $chat_id = 0): void
     {
-        $bot = null;
-
-        if ($chat_id === 0) {
-            $chat_id = $_ENV['telegram_chatid'];
-        }
-
         if ($_ENV['enable_telegram']) {
+            $bot = null;
+
+            if ($chat_id === 0) {
+                $chat_id = $_ENV['telegram_chatid'];
+            }
             // 发送给非群组时使用异步
             $async = ($chat_id !== $_ENV['telegram_chatid']);
             $bot = new Api($_ENV['telegram_token'], $async);
@@ -151,11 +147,6 @@ final class Telegram
         }
     }
 
-    public static function generateRandomLink(): string
-    {
-        return Tools::genRandomChar(16);
-    }
-
     /**
      * @throws RedisException
      */
@@ -180,7 +171,7 @@ final class Telegram
     public static function addBindSession($user): string
     {
         $redis = Cache::initRedis();
-        $token = self::generateRandomLink();
+        $token = Tools::genRandomChar(16);
 
         $redis->setex(
             'telegram_bind:' . $token,

+ 0 - 4
src/Utils/Telegram/Callback.php

@@ -14,7 +14,6 @@ use App\Models\Setting;
 use App\Models\SubscribeLog;
 use App\Services\Config;
 use App\Utils\Tools;
-use GeoIp2\Exception\AddressNotFoundException;
 use MaxMind\Db\Reader\InvalidDatabaseException;
 use Telegram\Bot\Api;
 use Telegram\Bot\Exceptions\TelegramSDKException;
@@ -70,7 +69,6 @@ final class Callback
      * @param Api $bot
      * @param CallbackQuery $Callback
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      * @throws TelegramSDKException
      */
@@ -281,7 +279,6 @@ final class Callback
     /**
      * 用户相关回调数据处理
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      * @throws TelegramSDKException
      */
@@ -388,7 +385,6 @@ final class Callback
      * 用户中心
      *
      * @throws TelegramSDKException
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
     public function userCenter(): void

+ 12 - 3
src/Utils/Tools.php

@@ -47,7 +47,6 @@ final class Tools
     /**
      * 查询IP归属
      *
-     * @throws AddressNotFoundException
      * @throws InvalidDatabaseException
      */
     public static function getIpLocation($ip): string
@@ -60,8 +59,18 @@ final class Tools
             $err_msg = 'GeoIP2 服务未配置';
         } else {
             $geoip = new GeoIP2();
-            $city = $geoip->getCity($ip);
-            $country = $geoip->getCountry($ip);
+
+            try {
+                $city = $geoip->getCity($ip);
+            } catch (AddressNotFoundException $e) {
+                $city = '未知城市';
+            }
+
+            try {
+                $country = $geoip->getCountry($ip);
+            } catch (AddressNotFoundException $e) {
+                $country = '未知国家';
+            }
         }
 
         if ($city !== null) {

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