diff --git a/gost.sql b/gost.sql index 60b054e..bf68c09 100644 --- a/gost.sql +++ b/gost.sql @@ -132,7 +132,7 @@ CREATE TABLE `user` ( -- INSERT INTO `user` (`id`, `user`, `pwd`, `role_id`, `exp_time`, `flow`, `in_flow`, `out_flow`, `flow_reset_time`, `num`, `created_time`, `updated_time`, `status`) VALUES -(1, 'admin_user', '3c85cdebade1c51cf64ca9f3c09d182d', 0, 1780480500000, 99999, 0, 0, 1, 99999, 1748914865000, 1751434735216, 1); +(1, 'admin_user', '3c85cdebade1c51cf64ca9f3c09d182d', 0, 2727251700000, 99999, 0, 0, 1, 99999, 1748914865000, 1751434735216, 1); -- -------------------------------------------------------- diff --git a/springboot-backend/src/main/java/com/admin/config/WebMvcConfig.java b/springboot-backend/src/main/java/com/admin/config/WebMvcConfig.java index c23c905..5e7dfab 100644 --- a/springboot-backend/src/main/java/com/admin/config/WebMvcConfig.java +++ b/springboot-backend/src/main/java/com/admin/config/WebMvcConfig.java @@ -57,6 +57,7 @@ public class WebMvcConfig implements WebMvcConfigurer { registry.addInterceptor(jwtInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/flow/**") + .excludePathPatterns("/api/v1/open_api/**") .excludePathPatterns("/api/v1/config/get") .excludePathPatterns("/api/v1/user/login"); } diff --git a/springboot-backend/src/main/java/com/admin/controller/OpenApiController.java b/springboot-backend/src/main/java/com/admin/controller/OpenApiController.java new file mode 100644 index 0000000..fd50d51 --- /dev/null +++ b/springboot-backend/src/main/java/com/admin/controller/OpenApiController.java @@ -0,0 +1,80 @@ +package com.admin.controller; + + +import com.admin.common.aop.LogAnnotation; +import com.admin.common.lang.R; +import javax.servlet.http.HttpServletResponse; + +import com.admin.common.utils.Md5Util; +import com.admin.entity.User; +import com.admin.entity.UserTunnel; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.springframework.web.bind.annotation.*; + +import java.util.Objects; + +@RestController +@CrossOrigin +@RequestMapping("/api/v1/open_api") +public class OpenApiController extends BaseController { + + @LogAnnotation + @GetMapping("/sub_store") + public R create( + @RequestParam("user") String user, + @RequestParam("pwd") String pwd, + @RequestParam(value = "tunnel", required = false, defaultValue = "-1") String tunnel, + HttpServletResponse response) { + + // 校验 user 是否为空 + if (user == null || user.isEmpty()) { + return R.err("用户不能为空"); + } + if (pwd == null || pwd.isEmpty()) { + return R.err("密码不能为空"); + } + + User userInfo = userService.getOne(new QueryWrapper().eq("user", user)); + if (userInfo == null) { + return R.err("鉴权失败"); + } + + String pwdMd5 = Md5Util.md5(pwd); + if (!Objects.equals(pwdMd5, userInfo.getPwd())) { + return R.err("鉴权失败"); + } + + final long GIGA = 1024L * 1024L * 1024L; + String headerValue; + + if ("-1".equals(tunnel)) { + headerValue = buildSubscriptionHeader( + userInfo.getOutFlow(), + userInfo.getInFlow(), + userInfo.getFlow() * GIGA, + userInfo.getExpTime() / 1000 + ); + } else { + UserTunnel tunnelInfo = userTunnelService.getById(tunnel); + if (tunnelInfo == null) return R.err("隧道不存在"); + if (!tunnelInfo.getUserId().toString().equals(userInfo.getId().toString())) return R.err("隧道不存在"); + headerValue = buildSubscriptionHeader( + tunnelInfo.getOutFlow(), + tunnelInfo.getInFlow(), + tunnelInfo.getFlow() * GIGA, + tunnelInfo.getExpTime() / 1000 + ); + } + + response.setHeader("ubscription-userinfo", headerValue); + return R.ok(); + } + + + + private String buildSubscriptionHeader(long upload, long download, long total, long expire) { + return String.format("upload=%d;download=%d;total=%d;expire=%d", upload, download, total, expire); + } + + +} diff --git a/springboot-backend/src/main/java/com/admin/entity/User.java b/springboot-backend/src/main/java/com/admin/entity/User.java index a858205..432566e 100644 --- a/springboot-backend/src/main/java/com/admin/entity/User.java +++ b/springboot-backend/src/main/java/com/admin/entity/User.java @@ -1,6 +1,8 @@ package com.admin.entity; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import lombok.EqualsAndHashCode; @@ -18,6 +20,27 @@ public class User extends BaseEntity { private static final long serialVersionUID = 1L; + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 创建时间(时间戳) + */ + private Long createdTime; + + /** + * 更新时间(时间戳) + */ + private Long updatedTime; + + /** + * 状态(0:正常,1:删除) + */ + private Integer status; + private String user; private String pwd; diff --git a/vite-frontend/src/config/site.ts b/vite-frontend/src/config/site.ts index dc35235..aef198b 100644 --- a/vite-frontend/src/config/site.ts +++ b/vite-frontend/src/config/site.ts @@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig; // 缓存相关常量 const CACHE_PREFIX = 'vite_config_'; -const VERSION = "1.1.0"; +const VERSION = "1.1.1"; // 缓存策略: // - 长期有效,不设置过期时间 diff --git a/vite-frontend/src/pages/dashboard.tsx b/vite-frontend/src/pages/dashboard.tsx index 9865e8a..60184ca 100644 --- a/vite-frontend/src/pages/dashboard.tsx +++ b/vite-frontend/src/pages/dashboard.tsx @@ -675,7 +675,7 @@ export default function DashboardPage() {
-

{tunnel.tunnelName}

+

{tunnel.tunnelName} ID: {tunnel.id}

{tunnel.tunnelFlow === 1 ? '单向计费' : '双向计费'} diff --git a/vite-frontend/src/pages/node.tsx b/vite-frontend/src/pages/node.tsx index f0345c8..7ee4e8e 100644 --- a/vite-frontend/src/pages/node.tsx +++ b/vite-frontend/src/pages/node.tsx @@ -537,6 +537,25 @@ export default function NodePage() {
+ {/* WebSocket支持说明 */} +
+
+
+ + + +
+
+ +

+ 节点实时监控依赖WebSocket连接。如果面板开启了反向代理或使用了CDN, + 请确保已开启WebSocket支持,否则节点状态可能不显示节点信息。 + 具体配置方法请自行搜索相关教程或询问AI助手。 +

+
+
+
+ {/* 节点列表 */} {loading ? (
@@ -694,7 +713,7 @@ export default function NodePage() { isLoading={node.copyLoading} className="flex-1 min-h-8" > - 复制 + 安装