mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-13 04:36:37 +08:00
修复部分浏览器判定为app的bug
This commit is contained in:
@@ -64,7 +64,6 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
NodeService nodeService;
|
NodeService nodeService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R createForward(ForwardDto forwardDto) {
|
public R createForward(ForwardDto forwardDto) {
|
||||||
// 1. 获取当前用户信息
|
// 1. 获取当前用户信息
|
||||||
@@ -105,7 +104,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. 调用Gost服务创建转发
|
// 7. 调用Gost服务创建转发
|
||||||
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter(), nodeInfo, permissionResult.getUserTunnel());
|
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter(), nodeInfo, permissionResult.getUserTunnel());
|
||||||
|
|
||||||
if (gostResult.getCode() != 0) {
|
if (gostResult.getCode() != 0) {
|
||||||
this.removeById(forward.getId());
|
this.removeById(forward.getId());
|
||||||
@@ -231,7 +230,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
gostResult = updateGostServicesWithTunnelChange(existForward, updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, nodeInfo, userTunnel);
|
gostResult = updateGostServicesWithTunnelChange(existForward, updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, nodeInfo, userTunnel);
|
||||||
} else {
|
} else {
|
||||||
// 隧道未变化时:直接更新配置
|
// 隧道未变化时:直接更新配置
|
||||||
gostResult = updateGostServices(updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, nodeInfo, userTunnel);
|
gostResult = updateGostServices(updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, nodeInfo, userTunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gostResult.getCode() != 0) {
|
if (gostResult.getCode() != 0) {
|
||||||
@@ -455,24 +454,23 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
return R.err("入口节点不存在");
|
return R.err("入口节点不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 解析目标地址,取第一个地址作为诊断目标
|
|
||||||
String[] remoteAddresses = forward.getRemoteAddr().split(",");
|
|
||||||
String targetAddress = remoteAddresses[0].trim();
|
|
||||||
|
|
||||||
// 提取IP和端口
|
|
||||||
String targetIp = extractIpFromAddress(targetAddress);
|
|
||||||
int targetPort = extractPortFromAddress(targetAddress);
|
|
||||||
if (targetIp == null || targetPort == -1) {
|
|
||||||
return R.err("无法解析目标地址: " + targetAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DiagnosisResult> results = new ArrayList<>();
|
List<DiagnosisResult> results = new ArrayList<>();
|
||||||
|
String[] remoteAddresses = forward.getRemoteAddr().split(",");
|
||||||
// 6. 根据隧道类型执行不同的诊断策略
|
// 6. 根据隧道类型执行不同的诊断策略
|
||||||
if (tunnel.getType() == TUNNEL_TYPE_PORT_FORWARD) {
|
if (tunnel.getType() == TUNNEL_TYPE_PORT_FORWARD) {
|
||||||
// 端口转发:入口节点直接TCP ping目标地址
|
// 端口转发:入口节点直接TCP ping目标地址
|
||||||
DiagnosisResult result = performTcpPingDiagnosis(inNode, targetIp, targetPort, "转发->目标");
|
for (String remoteAddress : remoteAddresses) {
|
||||||
results.add(result);
|
// 提取IP和端口
|
||||||
|
String targetIp = extractIpFromAddress(remoteAddress);
|
||||||
|
int targetPort = extractPortFromAddress(remoteAddress);
|
||||||
|
if (targetIp == null || targetPort == -1) {
|
||||||
|
return R.err("无法解析目标地址: " + remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagnosisResult result = performTcpPingDiagnosis(inNode, targetIp, targetPort, "转发->目标");
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 隧道转发:入口TCP ping出口,出口TCP ping目标
|
// 隧道转发:入口TCP ping出口,出口TCP ping目标
|
||||||
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
Node outNode = nodeService.getNodeById(tunnel.getOutNodeId());
|
||||||
@@ -485,8 +483,17 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
results.add(inToOutResult);
|
results.add(inToOutResult);
|
||||||
|
|
||||||
// 出口TCP ping目标
|
// 出口TCP ping目标
|
||||||
DiagnosisResult outToTargetResult = performTcpPingDiagnosis(outNode, targetIp, targetPort, "出口->目标");
|
for (String remoteAddress : remoteAddresses) {
|
||||||
results.add(outToTargetResult);
|
// 提取IP和端口
|
||||||
|
String targetIp = extractIpFromAddress(remoteAddress);
|
||||||
|
int targetPort = extractPortFromAddress(remoteAddress);
|
||||||
|
if (targetIp == null || targetPort == -1) {
|
||||||
|
return R.err("无法解析目标地址: " + remoteAddress);
|
||||||
|
}
|
||||||
|
DiagnosisResult outToTargetResult = performTcpPingDiagnosis(outNode, targetIp, targetPort, "出口->目标");
|
||||||
|
results.add(outToTargetResult);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 构建诊断报告
|
// 7. 构建诊断报告
|
||||||
@@ -521,8 +528,8 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
if (currentUser.getRoleId() != ADMIN_ROLE_ID) {
|
||||||
// 普通用户只能更新自己的转发
|
// 普通用户只能更新自己的转发
|
||||||
List<Long> forwardIds = forwardsList.stream()
|
List<Long> forwardIds = forwardsList.stream()
|
||||||
.map(item -> Long.valueOf(item.get("id").toString()))
|
.map(item -> Long.valueOf(item.get("id").toString()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 检查所有转发是否属于当前用户
|
// 检查所有转发是否属于当前用户
|
||||||
QueryWrapper<Forward> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<Forward> queryWrapper = new QueryWrapper<>();
|
||||||
@@ -633,9 +640,9 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
/**
|
/**
|
||||||
* 执行TCP ping诊断
|
* 执行TCP ping诊断
|
||||||
*
|
*
|
||||||
* @param node 执行TCP ping的节点
|
* @param node 执行TCP ping的节点
|
||||||
* @param targetIp 目标IP地址
|
* @param targetIp 目标IP地址
|
||||||
* @param port 目标端口
|
* @param port 目标端口
|
||||||
* @param description 诊断描述
|
* @param description 诊断描述
|
||||||
* @return 诊断结果
|
* @return 诊断结果
|
||||||
*/
|
*/
|
||||||
@@ -645,8 +652,8 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
JSONObject tcpPingData = new JSONObject();
|
JSONObject tcpPingData = new JSONObject();
|
||||||
tcpPingData.put("ip", targetIp);
|
tcpPingData.put("ip", targetIp);
|
||||||
tcpPingData.put("port", port);
|
tcpPingData.put("port", port);
|
||||||
tcpPingData.put("count", 4);
|
tcpPingData.put("count", 2);
|
||||||
tcpPingData.put("timeout", 5000); // 5秒超时
|
tcpPingData.put("timeout", 3000); // 5秒超时
|
||||||
|
|
||||||
// 发送TCP ping命令到节点
|
// 发送TCP ping命令到节点
|
||||||
GostDto gostResult = WebSocketServer.send_msg(node.getId(), tcpPingData, "TcpPing");
|
GostDto gostResult = WebSocketServer.send_msg(node.getId(), tcpPingData, "TcpPing");
|
||||||
@@ -742,7 +749,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
|
|
||||||
// 普通用户只能操作自己的转发
|
// 普通用户只能操作自己的转发
|
||||||
if (currentUser.getRoleId() != ADMIN_ROLE_ID &&
|
if (currentUser.getRoleId() != ADMIN_ROLE_ID &&
|
||||||
!Objects.equals(currentUser.getUserId(), forward.getUserId())) {
|
!Objects.equals(currentUser.getUserId(), forward.getUserId())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +797,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userTunnel.getStatus() != 1) {
|
if (userTunnel.getStatus() != 1) {
|
||||||
return UserPermissionResult.error("隧道被禁用");
|
return UserPermissionResult.error("隧道被禁用");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查隧道权限到期时间
|
// 检查隧道权限到期时间
|
||||||
@@ -942,7 +949,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
// 处理端口分配逻辑
|
// 处理端口分配逻辑
|
||||||
boolean tunnelChanged = !existForward.getTunnelId().equals(forwardUpdateDto.getTunnelId());
|
boolean tunnelChanged = !existForward.getTunnelId().equals(forwardUpdateDto.getTunnelId());
|
||||||
boolean inPortChanged = forwardUpdateDto.getInPort() != null &&
|
boolean inPortChanged = forwardUpdateDto.getInPort() != null &&
|
||||||
!Objects.equals(forwardUpdateDto.getInPort(), existForward.getInPort());
|
!Objects.equals(forwardUpdateDto.getInPort(), existForward.getInPort());
|
||||||
|
|
||||||
if (tunnelChanged || inPortChanged) {
|
if (tunnelChanged || inPortChanged) {
|
||||||
// 隧道变化或入口端口变化时需要重新分配
|
// 隧道变化或入口端口变化时需要重新分配
|
||||||
@@ -971,8 +978,8 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
/**
|
/**
|
||||||
* 创建Gost服务
|
* 创建Gost服务
|
||||||
*/
|
*/
|
||||||
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel);
|
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel);
|
||||||
|
|
||||||
// 隧道转发需要创建链和远程服务
|
// 隧道转发需要创建链和远程服务
|
||||||
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
|
||||||
@@ -1011,7 +1018,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
/**
|
/**
|
||||||
* 更新Gost服务
|
* 更新Gost服务
|
||||||
*/
|
*/
|
||||||
private R updateGostServices(Forward forward, Tunnel tunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
private R updateGostServices(Forward forward, Tunnel tunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||||
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel);
|
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel);
|
||||||
|
|
||||||
// 隧道转发需要更新链和远程服务
|
// 隧道转发需要更新链和远程服务
|
||||||
@@ -1046,7 +1053,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
/**
|
/**
|
||||||
* 隧道变化时更新Gost服务:先删除原配置,再创建新配置
|
* 隧道变化时更新Gost服务:先删除原配置,再创建新配置
|
||||||
*/
|
*/
|
||||||
private R updateGostServicesWithTunnelChange(Forward existForward, Forward updatedForward, Tunnel newTunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
private R updateGostServicesWithTunnelChange(Forward existForward, Forward updatedForward, Tunnel newTunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
|
||||||
// 1. 获取原隧道信息
|
// 1. 获取原隧道信息
|
||||||
Tunnel oldTunnel = tunnelService.getById(existForward.getTunnelId());
|
Tunnel oldTunnel = tunnelService.getById(existForward.getTunnelId());
|
||||||
if (oldTunnel == null) {
|
if (oldTunnel == null) {
|
||||||
@@ -1165,7 +1172,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
* 创建远程服务
|
* 创建远程服务
|
||||||
*/
|
*/
|
||||||
private R createRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
|
private R createRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
|
||||||
GostDto result = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
|
GostDto result = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
|
||||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1198,9 +1205,9 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
*/
|
*/
|
||||||
private R updateRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
|
private R updateRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
|
||||||
// 创建新远程服务
|
// 创建新远程服务
|
||||||
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
|
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
|
||||||
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||||
createResult = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(),protocol, forward.getStrategy(), interfaceName);
|
createResult = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
|
||||||
}
|
}
|
||||||
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
|
||||||
}
|
}
|
||||||
@@ -1212,7 +1219,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
|
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
|
||||||
|
|
||||||
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
|
||||||
result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
|
result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
|
||||||
@@ -1276,24 +1283,24 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
* 为隧道分配一个可用的入口端口(可排除指定的转发ID)
|
* 为隧道分配一个可用的入口端口(可排除指定的转发ID)
|
||||||
*/
|
*/
|
||||||
private Integer allocateInPort(Tunnel tunnel, Long excludeForwardId) {
|
private Integer allocateInPort(Tunnel tunnel, Long excludeForwardId) {
|
||||||
return allocatePortForNode(tunnel.getInNodeId(), excludeForwardId);
|
return allocatePortForNode(tunnel.getInNodeId(), excludeForwardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为隧道分配一个可用的出口端口(可排除指定的转发ID)
|
* 为隧道分配一个可用的出口端口(可排除指定的转发ID)
|
||||||
*/
|
*/
|
||||||
private Integer allocateOutPort(Tunnel tunnel, Long excludeForwardId) {
|
private Integer allocateOutPort(Tunnel tunnel, Long excludeForwardId) {
|
||||||
return allocatePortForNode(tunnel.getOutNodeId(), excludeForwardId);
|
return allocatePortForNode(tunnel.getOutNodeId(), excludeForwardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为指定节点分配一个可用端口(通用方法)
|
* 为指定节点分配一个可用端口(通用方法)
|
||||||
*
|
*
|
||||||
* @param nodeId 节点ID
|
* @param nodeId 节点ID
|
||||||
* @param excludeForwardId 要排除的转发ID
|
* @param excludeForwardId 要排除的转发ID
|
||||||
* @return 可用端口号,如果没有可用端口则返回null
|
* @return 可用端口号,如果没有可用端口则返回null
|
||||||
*/
|
*/
|
||||||
private Integer allocatePortForNode(Long nodeId, Long excludeForwardId) {
|
private Integer allocatePortForNode(Long nodeId, Long excludeForwardId) {
|
||||||
// 获取节点信息
|
// 获取节点信息
|
||||||
Node node = nodeService.getNodeById(nodeId);
|
Node node = nodeService.getNodeById(nodeId);
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
@@ -1315,7 +1322,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
/**
|
/**
|
||||||
* 获取指定节点上所有已被占用的端口(包括入口和出口端口)
|
* 获取指定节点上所有已被占用的端口(包括入口和出口端口)
|
||||||
*
|
*
|
||||||
* @param nodeId 节点ID
|
* @param nodeId 节点ID
|
||||||
* @param excludeForwardId 要排除的转发ID
|
* @param excludeForwardId 要排除的转发ID
|
||||||
* @return 已占用的端口集合
|
* @return 已占用的端口集合
|
||||||
*/
|
*/
|
||||||
@@ -1378,20 +1385,20 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
|
|||||||
public void updateForwardA(Forward forward) {
|
public void updateForwardA(Forward forward) {
|
||||||
Tunnel tunnel = validateTunnel(forward.getTunnelId());
|
Tunnel tunnel = validateTunnel(forward.getTunnelId());
|
||||||
if (tunnel == null) {
|
if (tunnel == null) {
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
UserTunnel userTunnel = getUserTunnel(forward.getUserId(), tunnel.getId().intValue());
|
UserTunnel userTunnel = getUserTunnel(forward.getUserId(), tunnel.getId().intValue());
|
||||||
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
NodeInfo nodeInfo = getRequiredNodes(tunnel);
|
||||||
if (nodeInfo.isHasError()) {
|
if (nodeInfo.isHasError()) {
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
Integer limiter;
|
Integer limiter;
|
||||||
if (userTunnel == null) {
|
if (userTunnel == null) {
|
||||||
limiter = null;
|
limiter = null;
|
||||||
}else {
|
} else {
|
||||||
limiter = userTunnel.getSpeedId();
|
limiter = userTunnel.getSpeedId();
|
||||||
}
|
}
|
||||||
updateGostServices(forward, tunnel, limiter, nodeInfo, userTunnel);
|
updateGostServices(forward, tunnel, limiter, nodeInfo, userTunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
VITE_API_BASE=http://192.168.100.9:6365
|
VITE_API_BASE=http://192.168.31.126:6365
|
||||||
|
|||||||
@@ -85,6 +85,16 @@
|
|||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 使用动态视口单位,避免移动端地址栏导致的额外滚动 */
|
||||||
|
@supports (height: 100dvh) {
|
||||||
|
#root { min-height: 100dvh; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iOS Safari 旧版本兼容 */
|
||||||
|
@supports (-webkit-touch-callout: none) {
|
||||||
|
#root { min-height: -webkit-fill-available; }
|
||||||
|
}
|
||||||
|
|
||||||
/* 防止加载时的闪烁 */
|
/* 防止加载时的闪烁 */
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
import { getPanelAddresses} from '@/utils/panel';
|
import { getPanelAddresses, isWebViewFunc} from '@/utils/panel';
|
||||||
// 检测是否在WebView中运行
|
|
||||||
const isWebView = (): boolean => {
|
|
||||||
if((window as any).JsInterface !== undefined) {
|
|
||||||
return true;
|
|
||||||
}else if((window as any).webkit && (window as any).webkit.messageHandlers) {
|
|
||||||
return true;
|
|
||||||
}else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
interface PanelAddress {
|
interface PanelAddress {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -34,7 +25,7 @@ function getWebViewPanelAddress() {
|
|||||||
let baseURL: string = '';
|
let baseURL: string = '';
|
||||||
|
|
||||||
export const reinitializeBaseURL = () => {
|
export const reinitializeBaseURL = () => {
|
||||||
if (isWebView()) {
|
if (isWebViewFunc()) {
|
||||||
getWebViewPanelAddress();
|
getWebViewPanelAddress();
|
||||||
} else {
|
} else {
|
||||||
baseURL = import.meta.env.VITE_API_BASE ? `${import.meta.env.VITE_API_BASE}/api/v1/` : '/api/v1/';
|
baseURL = import.meta.env.VITE_API_BASE ? `${import.meta.env.VITE_API_BASE}/api/v1/` : '/api/v1/';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
NavbarBrand,
|
NavbarBrand,
|
||||||
NavbarContent,
|
NavbarContent,
|
||||||
} from "@heroui/navbar";
|
} from "@heroui/navbar";
|
||||||
|
import { isWebViewFunc } from '@/utils/panel';
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { Logo } from "@/components/icons";
|
import { Logo } from "@/components/icons";
|
||||||
@@ -18,13 +19,7 @@ export const Navbar = () => {
|
|||||||
|
|
||||||
// 检测是否在WebView中运行
|
// 检测是否在WebView中运行
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if((window as any).JsInterface !== undefined) {
|
setIsWebView(isWebViewFunc());
|
||||||
setIsWebView(true);
|
|
||||||
}else if((window as any).webkit && (window as any).webkit.messageHandlers) {
|
|
||||||
setIsWebView(true);
|
|
||||||
}else {
|
|
||||||
setIsWebView(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
|
|||||||
|
|
||||||
// 缓存相关常量
|
// 缓存相关常量
|
||||||
const CACHE_PREFIX = 'vite_config_';
|
const CACHE_PREFIX = 'vite_config_';
|
||||||
const VERSION = "1.3.1";
|
const VERSION = "1.3.2";
|
||||||
const APP_VERSION = "1.0.1";
|
const APP_VERSION = "1.0.1";
|
||||||
|
|
||||||
const getInitialConfig = () => {
|
const getInitialConfig = () => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { Link } from "@heroui/link";
|
|
||||||
|
|
||||||
import { Navbar } from "@/components/navbar";
|
import { Navbar } from "@/components/navbar";
|
||||||
|
|
||||||
export default function DefaultLayout({
|
export default function DefaultLayout({
|
||||||
@@ -13,15 +11,6 @@ export default function DefaultLayout({
|
|||||||
<main className="container mx-auto max-w-7xl px-4 sm:px-6 flex-grow pt-4 sm:pt-16">
|
<main className="container mx-auto max-w-7xl px-4 sm:px-6 flex-grow pt-4 sm:pt-16">
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
<footer className="w-full flex items-center justify-center py-3">
|
|
||||||
<Link
|
|
||||||
isExternal
|
|
||||||
className="flex items-center gap-1 text-current"
|
|
||||||
href="https://heroui.com"
|
|
||||||
title="heroui.com homepage"
|
|
||||||
>
|
|
||||||
</Link>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default function ChangePasswordPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10 min-h-[calc(100vh-200px)]">
|
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10 min-h-[calc(100dvh-200px)]">
|
||||||
<div className="w-full max-w-lg">
|
<div className="w-full max-w-lg">
|
||||||
<Card className="w-full">
|
<Card className="w-full">
|
||||||
<CardHeader className="pb-0 pt-6 px-6 flex-col items-center">
|
<CardHeader className="pb-0 pt-6 px-6 flex-col items-center">
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const CONFIG_ITEMS: ConfigItem[] = [
|
|||||||
const getInitialConfigs = (): Record<string, string> => {
|
const getInitialConfigs = (): Record<string, string> => {
|
||||||
if (typeof window === 'undefined') return {};
|
if (typeof window === 'undefined') return {};
|
||||||
|
|
||||||
const configKeys = ['app_name', 'captcha_enabled', 'captcha_type'];
|
const configKeys = ['app_name', 'captcha_enabled', 'captcha_type', 'ip'];
|
||||||
const initialConfigs: Record<string, string> = {};
|
const initialConfigs: Record<string, string> = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useState, useEffect, useRef } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { isWebViewFunc } from '@/utils/panel';
|
||||||
import { siteConfig } from '@/config/site';
|
import { siteConfig } from '@/config/site';
|
||||||
import { title } from "@/components/primitives";
|
import { title } from "@/components/primitives";
|
||||||
import DefaultLayout from "@/layouts/default";
|
import DefaultLayout from "@/layouts/default";
|
||||||
@@ -65,13 +65,7 @@ export default function IndexPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
// 检测是否在WebView中运行
|
// 检测是否在WebView中运行
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if((window as any).JsInterface !== undefined) {
|
setIsWebView(isWebViewFunc());
|
||||||
setIsWebView(true);
|
|
||||||
}else if((window as any).webkit && (window as any).webkit.messageHandlers) {
|
|
||||||
setIsWebView(true);
|
|
||||||
}else {
|
|
||||||
setIsWebView(false);
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
// 验证表单
|
// 验证表单
|
||||||
const validateForm = (): boolean => {
|
const validateForm = (): boolean => {
|
||||||
@@ -259,7 +253,7 @@ export default function IndexPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<section className="flex flex-col items-center justify-center gap-4 py-4 sm:py-8 md:py-10 pb-20 min-h-[calc(100vh-120px)] sm:min-h-[calc(100vh-200px)]">
|
<section className="flex flex-col items-center justify-center gap-4 py-4 sm:py-8 md:py-10 pb-20 min-h-[calc(100dvh-120px)] sm:min-h-[calc(100dvh-200px)]">
|
||||||
<div className="w-full max-w-md px-4 sm:px-0">
|
<div className="w-full max-w-md px-4 sm:px-0">
|
||||||
<Card className="w-full">
|
<Card className="w-full">
|
||||||
<CardHeader className="pb-0 pt-6 px-6 flex-col items-center">
|
<CardHeader className="pb-0 pt-6 px-6 flex-col items-center">
|
||||||
@@ -311,7 +305,6 @@ export default function IndexPage() {
|
|||||||
|
|
||||||
{/* 版权信息 - 固定在底部,不占据布局空间 */}
|
{/* 版权信息 - 固定在底部,不占据布局空间 */}
|
||||||
|
|
||||||
{isWebView && (
|
|
||||||
<div className="fixed inset-x-0 bottom-4 text-center py-4">
|
<div className="fixed inset-x-0 bottom-4 text-center py-4">
|
||||||
<p className="text-xs text-gray-400 dark:text-gray-500">
|
<p className="text-xs text-gray-400 dark:text-gray-500">
|
||||||
Powered by{' '}
|
Powered by{' '}
|
||||||
@@ -325,10 +318,9 @@ export default function IndexPage() {
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
||||||
v{siteConfig.app_version}
|
v{ isWebView ? siteConfig.app_version : siteConfig.version}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, useDisclosure
|
|||||||
import { Input } from "@heroui/input";
|
import { Input } from "@heroui/input";
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { isWebViewFunc } from '@/utils/panel';
|
||||||
|
import { siteConfig } from '@/config/site';
|
||||||
import { updatePassword } from '@/api';
|
import { updatePassword } from '@/api';
|
||||||
import { safeLogout } from '@/utils/logout';
|
import { safeLogout } from '@/utils/logout';
|
||||||
|
|
||||||
interface PasswordForm {
|
interface PasswordForm {
|
||||||
newUsername: string;
|
newUsername: string;
|
||||||
currentPassword: string;
|
currentPassword: string;
|
||||||
@@ -16,6 +16,7 @@ interface PasswordForm {
|
|||||||
confirmPassword: string;
|
confirmPassword: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
path: string;
|
path: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -237,9 +238,29 @@ export default function ProfilePage() {
|
|||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<div className="fixed inset-x-0 bottom-20 text-center py-4">
|
||||||
|
<p className="text-xs text-gray-400 dark:text-gray-500">
|
||||||
|
Powered by{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/bqlpfy/flux-panel"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
|
||||||
|
>
|
||||||
|
flux-panel
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
||||||
|
v{ isWebViewFunc() ? siteConfig.app_version : siteConfig.version}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* 修改密码弹窗 */}
|
{/* 修改密码弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ export const SettingsPage = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
<div className="min-h-screen bg-gray-50 dark:bg-black">
|
||||||
{/* 顶部导航 */}
|
{/* 顶部导航 */}
|
||||||
<div className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
<div className="bg-white dark:bg-black border-b border-gray-200 dark:border-gray-700">
|
||||||
<div className="max-w-4xl mx-auto px-4 py-4">
|
<div className="max-w-4xl mx-auto px-4 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ export async function deletePanelAddress(name: string){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isWebViewFunc(){
|
||||||
|
if((window as any).JsInterface !== undefined && (window as any).JsInterface.getPanelAddresses !== undefined) {
|
||||||
|
return true;
|
||||||
|
}else if((window as any).webkit && (window as any).webkit.messageHandlers && (window as any).webkit.messageHandlers.getPanelAddresses !== undefined) {
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 验证面板地址格式
|
// 验证面板地址格式
|
||||||
export function validatePanelAddress(address: string): boolean {
|
export function validatePanelAddress(address: string): boolean {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user