添加subStore接口

This commit is contained in:
qaq
2025-08-01 12:57:40 +08:00
parent 4e8e366f75
commit 73c1af715e
7 changed files with 127 additions and 4 deletions
+1 -1
View File
@@ -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);
-- --------------------------------------------------------
@@ -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");
}
@@ -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<User>().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);
}
}
@@ -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;
+1 -1
View File
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
// 缓存相关常量
const CACHE_PREFIX = 'vite_config_';
const VERSION = "1.1.0";
const VERSION = "1.1.1";
// 缓存策略:
// - 长期有效,不设置过期时间
+1 -1
View File
@@ -675,7 +675,7 @@ export default function DashboardPage() {
<div key={tunnel.id} className="border border-gray-200 dark:border-default-100 rounded-lg p-3 lg:p-4 hover:shadow-md transition-shadow">
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 mb-3">
<div>
<h3 className="font-semibold text-foreground">{tunnel.tunnelName}</h3>
<h3 className="font-semibold text-foreground">{tunnel.tunnelName} ID: {tunnel.id}</h3>
<div className="flex flex-wrap items-center gap-2 mt-1">
<span className={`px-2 py-1 rounded-md text-xs font-medium ${tunnel.tunnelFlow === 1 ? 'bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-300' : 'bg-orange-100 dark:bg-orange-500/20 text-orange-700 dark:text-orange-300'}`}>
{tunnel.tunnelFlow === 1 ? '单向计费' : '双向计费'}
+20 -1
View File
@@ -537,6 +537,25 @@ export default function NodePage() {
</div>
</div>
{/* WebSocket支持说明 */}
<div className="mb-6 p-4 bg-warning-50 dark:bg-warning-100/10 border border-warning-200 dark:border-warning-300/20 rounded-lg">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 mt-0.5">
<svg className="w-5 h-5 text-warning-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
</div>
<div className="flex-1">
<p className="text-sm text-warning-700 dark:text-warning-300">
节点实时监控依赖WebSocket连接。如果面板开启了<strong>反向代理</strong>或使用了<strong>CDN</strong>,
请确保已开启WebSocket支持,否则节点状态可能不显示节点信息。
具体配置方法请自行搜索相关教程或询问AI助手。
</p>
</div>
</div>
</div>
{/* 节点列表 */}
{loading ? (
<div className="flex items-center justify-center h-64">
@@ -694,7 +713,7 @@ export default function NodePage() {
isLoading={node.copyLoading}
className="flex-1 min-h-8"
>
复制
安装
</Button>
<Button
size="sm"