mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-03 01:06:36 +08:00
style: 美化用户隧道权限分配表单UI并统一代码格式
- 使用HeroUI Checkbox组件替换原生checkbox - 重构隧道列表为card-based tile风格 - 添加选中/未选中/已分配状态视觉区分 - 应用eslint --fix代码格式统一 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { getRoleIdFromToken, isTokenValid } from './jwt';
|
||||
import { getRoleIdFromToken, isTokenValid } from "./jwt";
|
||||
|
||||
/**
|
||||
* 权限工具类
|
||||
@@ -9,7 +9,7 @@ import { getRoleIdFromToken, isTokenValid } from './jwt';
|
||||
* @returns token
|
||||
*/
|
||||
export function getToken(): string | null {
|
||||
return localStorage.getItem('token');
|
||||
return localStorage.getItem("token");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,9 +18,11 @@ export function getToken(): string | null {
|
||||
*/
|
||||
export function getCurrentUserRoleId(): number | null {
|
||||
const token = getToken();
|
||||
|
||||
if (!token || !isTokenValid(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getRoleIdFromToken(token);
|
||||
}
|
||||
|
||||
@@ -30,6 +32,7 @@ export function getCurrentUserRoleId(): number | null {
|
||||
*/
|
||||
export function isAdmin(): boolean {
|
||||
const roleId = getCurrentUserRoleId();
|
||||
|
||||
return roleId === 0;
|
||||
}
|
||||
|
||||
@@ -40,6 +43,7 @@ export function isAdmin(): boolean {
|
||||
*/
|
||||
export function hasRole(targetRoleId: number): boolean {
|
||||
const roleId = getCurrentUserRoleId();
|
||||
|
||||
return roleId === targetRoleId;
|
||||
}
|
||||
|
||||
@@ -49,6 +53,7 @@ export function hasRole(targetRoleId: number): boolean {
|
||||
*/
|
||||
export function isLoggedIn(): boolean {
|
||||
const token = getToken();
|
||||
|
||||
return token ? isTokenValid(token) : false;
|
||||
}
|
||||
|
||||
@@ -59,14 +64,16 @@ export function isLoggedIn(): boolean {
|
||||
* @returns 包装后的函数
|
||||
*/
|
||||
export function requireAdmin<T extends (...args: any[]) => any>(
|
||||
fn: T,
|
||||
errorMsg: string = '权限不足,仅管理员可操作'
|
||||
fn: T,
|
||||
errorMsg: string = "权限不足,仅管理员可操作",
|
||||
): T {
|
||||
return ((...args: Parameters<T>) => {
|
||||
if (!isAdmin()) {
|
||||
console.warn(errorMsg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return fn(...args);
|
||||
}) as T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,14 @@ interface JWTPayload {
|
||||
function getPayloadFromToken(token: string): JWTPayload | null {
|
||||
try {
|
||||
if (!token) return null;
|
||||
|
||||
const parts = token.split('.');
|
||||
|
||||
const parts = token.split(".");
|
||||
|
||||
if (parts.length !== 3) return null;
|
||||
|
||||
|
||||
const encodedPayload = parts[1];
|
||||
const decodedPayload = atob(encodedPayload);
|
||||
|
||||
return JSON.parse(decodedPayload) as JWTPayload;
|
||||
} catch (error) {
|
||||
return null;
|
||||
@@ -37,6 +39,7 @@ function getPayloadFromToken(token: string): JWTPayload | null {
|
||||
*/
|
||||
export function getUserIdFromToken(token: string): number | null {
|
||||
const payload = getPayloadFromToken(token);
|
||||
|
||||
return payload ? parseInt(payload.sub) : null;
|
||||
}
|
||||
|
||||
@@ -47,6 +50,7 @@ export function getUserIdFromToken(token: string): number | null {
|
||||
*/
|
||||
export function getRoleIdFromToken(token: string): number | null {
|
||||
const payload = getPayloadFromToken(token);
|
||||
|
||||
return payload ? payload.role_id : null;
|
||||
}
|
||||
|
||||
@@ -57,6 +61,7 @@ export function getRoleIdFromToken(token: string): number | null {
|
||||
*/
|
||||
export function getUsernameFromToken(token: string): string | null {
|
||||
const payload = getPayloadFromToken(token);
|
||||
|
||||
return payload ? payload.user : null;
|
||||
}
|
||||
|
||||
@@ -67,9 +72,11 @@ export function getUsernameFromToken(token: string): string | null {
|
||||
*/
|
||||
export function isTokenValid(token: string): boolean {
|
||||
const payload = getPayloadFromToken(token);
|
||||
|
||||
if (!payload) return false;
|
||||
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
return payload.exp > now;
|
||||
}
|
||||
|
||||
@@ -80,34 +87,38 @@ export const JwtUtil = {
|
||||
* @returns 用户ID
|
||||
*/
|
||||
getUserIdFromToken(): number | null {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
return token ? getUserIdFromToken(token) : null;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 从localStorage获取token并解析角色ID
|
||||
* @returns 角色ID
|
||||
*/
|
||||
getRoleIdFromToken(): number | null {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
return token ? getRoleIdFromToken(token) : null;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 从localStorage获取token并解析用户名
|
||||
* @returns 用户名
|
||||
*/
|
||||
getUsernameFromToken(): string | null {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
return token ? getUsernameFromToken(token) : null;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 验证localStorage中的token是否有效
|
||||
* @returns 是否有效
|
||||
*/
|
||||
isTokenValid(): boolean {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
return token ? isTokenValid(token) : false;
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
*/
|
||||
export const safeLogout = () => {
|
||||
localStorage.clear();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,49 +1,66 @@
|
||||
|
||||
|
||||
|
||||
// 获取面板地址列表
|
||||
export async function getPanelAddresses(callback: string = "setPanelAddresses"){
|
||||
if ((window as any).JsInterface && (window as any).JsInterface.getPanelAddresses) {
|
||||
(window as any).JsInterface.getPanelAddresses(callback);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.getPanelAddresses.postMessage(callback);
|
||||
}
|
||||
|
||||
export async function getPanelAddresses(
|
||||
callback: string = "setPanelAddresses",
|
||||
) {
|
||||
if (
|
||||
(window as any).JsInterface &&
|
||||
(window as any).JsInterface.getPanelAddresses
|
||||
) {
|
||||
(window as any).JsInterface.getPanelAddresses(callback);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.getPanelAddresses.postMessage(
|
||||
callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存面板地址
|
||||
export async function savePanelAddress(name: string, address: string){
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.savePanelAddress(name, address);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.savePanelAddress.postMessage({ name, address });
|
||||
}
|
||||
export async function savePanelAddress(name: string, address: string) {
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.savePanelAddress(name, address);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.savePanelAddress.postMessage({
|
||||
name,
|
||||
address,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 设置当前面板地址
|
||||
export async function setCurrentPanelAddress(name: string) {
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.setCurrentPanelAddress(name);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.setCurrentPanelAddress.postMessage({ name });
|
||||
}
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.setCurrentPanelAddress(name);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.setCurrentPanelAddress.postMessage({
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 删除面板地址
|
||||
export async function deletePanelAddress(name: string){
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.deletePanelAddress(name);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.deletePanelAddress.postMessage({ name });
|
||||
}
|
||||
export async function deletePanelAddress(name: string) {
|
||||
if ((window as any).JsInterface) {
|
||||
(window as any).JsInterface.deletePanelAddress(name);
|
||||
} else if ((window as any).webkit && (window as any).webkit.messageHandlers) {
|
||||
(window as any).webkit.messageHandlers.deletePanelAddress.postMessage({
|
||||
name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function isWebViewFunc(){
|
||||
if((window as any).JsInterface !== undefined && (window as any).JsInterface.getPanelAddresses !== undefined) {
|
||||
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) {
|
||||
} else if (
|
||||
(window as any).webkit &&
|
||||
(window as any).webkit.messageHandlers &&
|
||||
(window as any).webkit.messageHandlers.getPanelAddresses !== undefined
|
||||
) {
|
||||
return true;
|
||||
}else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -52,52 +69,58 @@ export function isWebViewFunc(){
|
||||
export function validatePanelAddress(address: string): boolean {
|
||||
try {
|
||||
// 基本格式检查:必须以 http:// 或 https:// 开头
|
||||
if (!address.startsWith('http://') && !address.startsWith('https://')) {
|
||||
if (!address.startsWith("http://") && !address.startsWith("https://")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用URL构造函数验证完整URL格式
|
||||
const url = new URL(address);
|
||||
|
||||
|
||||
// 检查主机名不能为空
|
||||
if (!url.hostname || url.hostname.trim() === '') {
|
||||
if (!url.hostname || url.hostname.trim() === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查主机名
|
||||
const hostname = url.hostname;
|
||||
|
||||
|
||||
// 支持 localhost
|
||||
if (hostname === 'localhost') {
|
||||
if (hostname === "localhost") {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 支持 IPv4 地址
|
||||
const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
|
||||
if (ipv4Pattern.test(hostname)) {
|
||||
const parts = hostname.split('.');
|
||||
return parts.every(part => {
|
||||
const parts = hostname.split(".");
|
||||
|
||||
return parts.every((part) => {
|
||||
const num = parseInt(part);
|
||||
|
||||
return num >= 0 && num <= 255;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 支持 IPv6 地址
|
||||
const ipv6Pattern = /^\[([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\]$|^\[([0-9a-fA-F]{1,4}:)*:([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4}\]$/;
|
||||
const ipv6Pattern =
|
||||
/^\[([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\]$|^\[([0-9a-fA-F]{1,4}:)*:([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4}\]$/;
|
||||
|
||||
if (ipv6Pattern.test(hostname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 支持域名
|
||||
const domainPattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;
|
||||
const domainPattern =
|
||||
/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;
|
||||
|
||||
if (domainPattern.test(hostname)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
// URL构造函数失败说明格式不正确
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user