Refactor: Rename NanoKVM to BatchuKVM and update server URL
This commit is contained in:
23
web/src/lib/cookie.ts
Normal file
23
web/src/lib/cookie.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const COOKIE_TOKEN_KEY = 'nano-kvm-token';
|
||||
|
||||
export function existToken() {
|
||||
const token = Cookies.get(COOKIE_TOKEN_KEY);
|
||||
return !!token;
|
||||
}
|
||||
|
||||
export function getToken() {
|
||||
const token = Cookies.get(COOKIE_TOKEN_KEY);
|
||||
if (!token) return null;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
export function setToken(token: string) {
|
||||
Cookies.set(COOKIE_TOKEN_KEY, token, { expires: 30 });
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
Cookies.remove(COOKIE_TOKEN_KEY);
|
||||
}
|
||||
9
web/src/lib/encrypt.ts
Normal file
9
web/src/lib/encrypt.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
// This key is only used to prevent the data from being transmitted in plaintext.
|
||||
const SECRET_KEY = 'NanoKVM-KOREA-TestKey-2512092155';
|
||||
|
||||
export function encrypt(data: string) {
|
||||
const dataEncrypt = CryptoJS.AES.encrypt(data, SECRET_KEY).toString();
|
||||
return encodeURIComponent(dataEncrypt);
|
||||
}
|
||||
74
web/src/lib/http.ts
Normal file
74
web/src/lib/http.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { removeToken } from '@/lib/cookie.ts';
|
||||
import { getBaseUrl } from '@/lib/service.ts';
|
||||
|
||||
type Response = {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: any;
|
||||
};
|
||||
|
||||
class Http {
|
||||
private instance: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
const baseURL = getBaseUrl('http');
|
||||
const withCredentials = (import.meta.env.VITE_WITH_CREDENTIALS as string) !== 'false';
|
||||
|
||||
this.instance = axios.create({
|
||||
baseURL,
|
||||
withCredentials,
|
||||
timeout: 60 * 1000
|
||||
});
|
||||
|
||||
this.setInterceptors();
|
||||
}
|
||||
|
||||
private setInterceptors() {
|
||||
this.instance.interceptors.request.use((config) => {
|
||||
if (config.headers) {
|
||||
config.headers.Accept = 'application/json';
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
this.instance.interceptors.response.use(
|
||||
(response) => {
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
const code = error.response?.status;
|
||||
if (code === 401) {
|
||||
removeToken();
|
||||
window.location.reload();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public get(url: string, params?: any): Promise<Response> {
|
||||
return this.instance.request({
|
||||
method: 'get',
|
||||
url,
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
public post(url: string, data?: any): Promise<Response> {
|
||||
return this.instance.request({
|
||||
method: 'post',
|
||||
url,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
public request(config: AxiosRequestConfig): Promise<Response> {
|
||||
return this.instance.request(config);
|
||||
}
|
||||
}
|
||||
|
||||
export const http = new Http();
|
||||
196
web/src/lib/localstorage.ts
Normal file
196
web/src/lib/localstorage.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { Resolution } from '@/types';
|
||||
|
||||
const LANGUAGE_KEY = 'nano-kvm-language';
|
||||
const VIDEO_MODE_KEY = 'nano-kvm-vide-mode';
|
||||
const WEB_RESOLUTION_KEY = 'nano-kvm-web-resolution';
|
||||
const FPS_KEY = 'nano-kvm-fps';
|
||||
const QUALITY_KEY = 'nano-kvm-quality';
|
||||
const GOP_KEY = 'nano-kvm-gop';
|
||||
const FRAME_DETECT_KEY = 'nano-kvm-frame-detect';
|
||||
const MOUSE_STYLE_KEY = 'nano-kvm-mouse-style';
|
||||
const MOUSE_MODE_KEY = 'nano-kvm-mouse-mode';
|
||||
const MOUSE_SCROLL_INTERVAL_KEY = 'nanokvm-kvm-mouse-scroll-interval';
|
||||
const SKIP_UPDATE_KEY = 'nano-kvm-check-update';
|
||||
const KEYBOARD_SYSTEM_KEY = 'nano-kvm-keyboard-system';
|
||||
const KEYBOARD_LANGUAGE_KEY = 'nano-kvm-keyboard-language';
|
||||
const SKIP_MODIFY_PASSWORD_KEY = 'nano-kvm-skip-modify-password';
|
||||
const MENU_DISABLED_ITEMS_KEY = 'nano-kvm-menu-disabled-items';
|
||||
const POWER_CONFIRM_KEY = 'nano-kvm-power-confirm';
|
||||
|
||||
type ItemWithExpiry = {
|
||||
value: string;
|
||||
expiry: number;
|
||||
};
|
||||
|
||||
// set the value with expiration time (unit: milliseconds)
|
||||
function setWithExpiry(key: string, value: string, ttl: number) {
|
||||
const now = new Date();
|
||||
|
||||
const item: ItemWithExpiry = {
|
||||
value: value,
|
||||
expiry: now.getTime() + ttl
|
||||
};
|
||||
|
||||
localStorage.setItem(key, JSON.stringify(item));
|
||||
}
|
||||
|
||||
// get the value with expiration time
|
||||
function getWithExpiry(key: string) {
|
||||
const itemStr = localStorage.getItem(key);
|
||||
if (!itemStr) return null;
|
||||
|
||||
const item: ItemWithExpiry = JSON.parse(itemStr);
|
||||
const now = new Date();
|
||||
if (now.getTime() > item.expiry) {
|
||||
localStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
return item.value;
|
||||
}
|
||||
|
||||
export function getLanguage() {
|
||||
return localStorage.getItem(LANGUAGE_KEY);
|
||||
}
|
||||
|
||||
export function setLanguage(language: string) {
|
||||
localStorage.setItem(LANGUAGE_KEY, language);
|
||||
}
|
||||
|
||||
export function getVideoMode() {
|
||||
return localStorage.getItem(VIDEO_MODE_KEY);
|
||||
}
|
||||
|
||||
export function setVideoMode(mode: string) {
|
||||
localStorage.setItem(VIDEO_MODE_KEY, mode);
|
||||
}
|
||||
|
||||
export function getResolution(): Resolution | null {
|
||||
const resolution = localStorage.getItem(WEB_RESOLUTION_KEY);
|
||||
if (resolution) {
|
||||
const obj = JSON.parse(window.atob(resolution));
|
||||
return obj as Resolution;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function setResolution(resolution: Resolution) {
|
||||
localStorage.setItem(WEB_RESOLUTION_KEY, window.btoa(JSON.stringify(resolution)));
|
||||
}
|
||||
|
||||
export function getFps() {
|
||||
const fps = localStorage.getItem(FPS_KEY);
|
||||
return fps ? Number(fps) : null;
|
||||
}
|
||||
|
||||
export function setFps(fps: number) {
|
||||
localStorage.setItem(FPS_KEY, String(fps));
|
||||
}
|
||||
|
||||
export function getQuality() {
|
||||
const quality = localStorage.getItem(QUALITY_KEY);
|
||||
return quality ? Number(quality) : null;
|
||||
}
|
||||
|
||||
export function setQuality(quality: number) {
|
||||
localStorage.setItem(QUALITY_KEY, String(quality));
|
||||
}
|
||||
|
||||
export function getGop() {
|
||||
const gop = localStorage.getItem(GOP_KEY);
|
||||
return gop ? Number(gop) : null;
|
||||
}
|
||||
|
||||
export function setGop(gop: number) {
|
||||
localStorage.setItem(GOP_KEY, String(gop));
|
||||
}
|
||||
|
||||
export function getFrameDetect(): boolean {
|
||||
const enabled = localStorage.getItem(FRAME_DETECT_KEY);
|
||||
return enabled === 'true';
|
||||
}
|
||||
|
||||
export function setFrameDetect(enabled: boolean) {
|
||||
localStorage.setItem(FRAME_DETECT_KEY, String(enabled));
|
||||
}
|
||||
|
||||
export function getMouseStyle() {
|
||||
return localStorage.getItem(MOUSE_STYLE_KEY);
|
||||
}
|
||||
|
||||
export function setMouseStyle(mouse: string) {
|
||||
localStorage.setItem(MOUSE_STYLE_KEY, mouse);
|
||||
}
|
||||
|
||||
export function getMouseMode() {
|
||||
return localStorage.getItem(MOUSE_MODE_KEY);
|
||||
}
|
||||
|
||||
export function setMouseMode(mouse: string) {
|
||||
localStorage.setItem(MOUSE_MODE_KEY, mouse);
|
||||
}
|
||||
|
||||
export function getMouseScrollInterval() {
|
||||
const interval = localStorage.getItem(MOUSE_SCROLL_INTERVAL_KEY);
|
||||
return interval ? Number(interval) : null;
|
||||
}
|
||||
|
||||
export function setMouseScrollInterval(interval: number): void {
|
||||
localStorage.setItem(MOUSE_SCROLL_INTERVAL_KEY, String(interval));
|
||||
}
|
||||
|
||||
export function getSkipUpdate() {
|
||||
const skip = getWithExpiry(SKIP_UPDATE_KEY);
|
||||
return skip === 'true';
|
||||
}
|
||||
|
||||
export function setSkipUpdate(skip: boolean) {
|
||||
const expiry = 3 * 24 * 60 * 60 * 1000; // 3 days
|
||||
setWithExpiry(SKIP_UPDATE_KEY, String(skip), expiry);
|
||||
}
|
||||
|
||||
export function setKeyboardSystem(system: string) {
|
||||
localStorage.setItem(KEYBOARD_SYSTEM_KEY, system);
|
||||
}
|
||||
|
||||
export function getKeyboardSystem() {
|
||||
return localStorage.getItem(KEYBOARD_SYSTEM_KEY);
|
||||
}
|
||||
|
||||
export function setKeyboardLanguage(language: string) {
|
||||
localStorage.setItem(KEYBOARD_LANGUAGE_KEY, language);
|
||||
}
|
||||
|
||||
export function getKeyboardLanguage() {
|
||||
return localStorage.getItem(KEYBOARD_LANGUAGE_KEY);
|
||||
}
|
||||
|
||||
export function setSkipModifyPassword(skip: boolean) {
|
||||
const expiry = 3 * 24 * 60 * 60 * 1000; // 3 days
|
||||
setWithExpiry(SKIP_MODIFY_PASSWORD_KEY, String(skip), expiry);
|
||||
}
|
||||
|
||||
export function getSkipModifyPassword() {
|
||||
const skip = getWithExpiry(SKIP_MODIFY_PASSWORD_KEY);
|
||||
return skip === 'true';
|
||||
}
|
||||
|
||||
export function setMenuDisabledItems(items: string[]) {
|
||||
const value = JSON.stringify(items);
|
||||
localStorage.setItem(MENU_DISABLED_ITEMS_KEY, value);
|
||||
}
|
||||
|
||||
export function getMenuDisabledItems(): string[] {
|
||||
const value = localStorage.getItem(MENU_DISABLED_ITEMS_KEY);
|
||||
return value ? JSON.parse(value) : [];
|
||||
}
|
||||
|
||||
export function getPowerConfirm() {
|
||||
const enabled = localStorage.getItem(POWER_CONFIRM_KEY);
|
||||
return enabled === 'true';
|
||||
}
|
||||
|
||||
export function setPowerConfirm(enabled: boolean) {
|
||||
localStorage.setItem(POWER_CONFIRM_KEY, String(enabled));
|
||||
}
|
||||
21
web/src/lib/service.ts
Normal file
21
web/src/lib/service.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export function getHostname(): string {
|
||||
const ip = import.meta.env.VITE_SERVER_IP as string;
|
||||
return ip ? ip : window.location.hostname;
|
||||
}
|
||||
|
||||
export function getPort(): string {
|
||||
const port = import.meta.env.VITE_SERVER_PORT as string;
|
||||
return port ? port : window.location.port;
|
||||
}
|
||||
|
||||
export function getBaseUrl(type: 'http' | 'ws'): string {
|
||||
let protocol = window.location.protocol;
|
||||
if (type === 'ws') {
|
||||
protocol = protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
}
|
||||
|
||||
const hostname = getHostname();
|
||||
const port = getPort();
|
||||
|
||||
return `${protocol}//${hostname}:${port}`;
|
||||
}
|
||||
66
web/src/lib/websocket.ts
Normal file
66
web/src/lib/websocket.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { IMessageEvent, w3cwebsocket as W3cWebSocket } from 'websocket';
|
||||
|
||||
import { getBaseUrl } from '@/lib/service.ts';
|
||||
|
||||
type Event = (message: IMessageEvent) => void;
|
||||
|
||||
const eventMap: Map<string, Event> = new Map<string, Event>();
|
||||
|
||||
class WsClient {
|
||||
private readonly url: string;
|
||||
private instance: W3cWebSocket;
|
||||
|
||||
constructor() {
|
||||
this.url = `${getBaseUrl('ws')}/api/ws`;
|
||||
this.instance = new W3cWebSocket(this.url);
|
||||
this.setEvents();
|
||||
}
|
||||
|
||||
public connect() {
|
||||
this.close();
|
||||
|
||||
this.instance = new W3cWebSocket(this.url);
|
||||
this.setEvents();
|
||||
}
|
||||
|
||||
public send(data: number[]) {
|
||||
if (this.instance.readyState !== W3cWebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = JSON.stringify(data);
|
||||
this.instance.send(message);
|
||||
}
|
||||
|
||||
public close() {
|
||||
if (this.instance.readyState === W3cWebSocket.OPEN) {
|
||||
this.instance.close();
|
||||
}
|
||||
}
|
||||
|
||||
public register(type: string, fn: (message: IMessageEvent) => void) {
|
||||
eventMap.set(type, fn);
|
||||
|
||||
this.setEvents();
|
||||
}
|
||||
|
||||
public unregister(type: string) {
|
||||
eventMap.delete(type);
|
||||
|
||||
this.setEvents();
|
||||
}
|
||||
|
||||
private setEvents() {
|
||||
this.instance.onmessage = (message) => {
|
||||
const data = JSON.parse(message.data as string);
|
||||
if (!data) return;
|
||||
|
||||
const fn = eventMap.get(data.type);
|
||||
if (!fn) return;
|
||||
|
||||
fn(message);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const client = new WsClient();
|
||||
Reference in New Issue
Block a user