mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
refactor: centralize API client and config caching
This commit is contained in:
26
app.js
26
app.js
@@ -31,14 +31,24 @@ import {
|
|||||||
// 核心服务导入
|
// 核心服务导入
|
||||||
import { createErrorHandler } from './src/core/error-handler.js';
|
import { createErrorHandler } from './src/core/error-handler.js';
|
||||||
import { connectionModule } from './src/core/connection.js';
|
import { connectionModule } from './src/core/connection.js';
|
||||||
|
import { ApiClient } from './src/core/api-client.js';
|
||||||
|
import { ConfigService } from './src/core/config-service.js';
|
||||||
|
import { createEventBus } from './src/core/event-bus.js';
|
||||||
|
|
||||||
// CLI Proxy API 管理界面 JavaScript
|
// CLI Proxy API 管理界面 JavaScript
|
||||||
class CLIProxyManager {
|
class CLIProxyManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// 仅保存基础地址(不含 /v0/management),请求时自动补齐
|
// 事件总线
|
||||||
|
this.events = createEventBus();
|
||||||
|
|
||||||
|
// API 客户端(规范化基础地址、封装请求)
|
||||||
|
this.apiClient = new ApiClient({
|
||||||
|
onVersionUpdate: (headers) => this.updateVersionFromHeaders(headers)
|
||||||
|
});
|
||||||
const detectedBase = this.detectApiBaseFromLocation();
|
const detectedBase = this.detectApiBaseFromLocation();
|
||||||
this.apiBase = detectedBase;
|
this.apiClient.setApiBase(detectedBase);
|
||||||
this.apiUrl = this.computeApiUrl(this.apiBase);
|
this.apiBase = this.apiClient.apiBase;
|
||||||
|
this.apiUrl = this.apiClient.apiUrl;
|
||||||
this.managementKey = '';
|
this.managementKey = '';
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
this.isLoggedIn = false;
|
this.isLoggedIn = false;
|
||||||
@@ -46,10 +56,14 @@ class CLIProxyManager {
|
|||||||
this.serverVersion = null;
|
this.serverVersion = null;
|
||||||
this.serverBuildDate = null;
|
this.serverBuildDate = null;
|
||||||
|
|
||||||
// 配置缓存 - 改为分段缓存
|
// 配置缓存 - 改为分段缓存(交由 ConfigService 管理)
|
||||||
this.configCache = {}; // 改为对象,按配置段缓存
|
|
||||||
this.cacheTimestamps = {}; // 每个配置段的时间戳
|
|
||||||
this.cacheExpiry = CACHE_EXPIRY_MS;
|
this.cacheExpiry = CACHE_EXPIRY_MS;
|
||||||
|
this.configService = new ConfigService({
|
||||||
|
apiClient: this.apiClient,
|
||||||
|
cacheExpiry: this.cacheExpiry
|
||||||
|
});
|
||||||
|
this.configCache = this.configService.cache;
|
||||||
|
this.cacheTimestamps = this.configService.cacheTimestamps;
|
||||||
|
|
||||||
// 状态更新定时器
|
// 状态更新定时器
|
||||||
this.statusUpdateTimer = null;
|
this.statusUpdateTimer = null;
|
||||||
|
|||||||
62
src/core/api-client.js
Normal file
62
src/core/api-client.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// API 客户端:负责规范化基础地址、构造完整 URL、发送请求并回传版本信息
|
||||||
|
export class ApiClient {
|
||||||
|
constructor({ apiBase = '', managementKey = '', onVersionUpdate = null } = {}) {
|
||||||
|
this.apiBase = '';
|
||||||
|
this.apiUrl = '';
|
||||||
|
this.managementKey = managementKey || '';
|
||||||
|
this.onVersionUpdate = onVersionUpdate;
|
||||||
|
this.setApiBase(apiBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeBase(input) {
|
||||||
|
let base = (input || '').trim();
|
||||||
|
if (!base) return '';
|
||||||
|
base = base.replace(/\/?v0\/management\/?$/i, '');
|
||||||
|
base = base.replace(/\/+$/i, '');
|
||||||
|
if (!/^https?:\/\//i.test(base)) {
|
||||||
|
base = 'http://' + base;
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
computeApiUrl(base) {
|
||||||
|
const normalized = this.normalizeBase(base);
|
||||||
|
if (!normalized) return '';
|
||||||
|
return normalized.replace(/\/$/, '') + '/v0/management';
|
||||||
|
}
|
||||||
|
|
||||||
|
setApiBase(newBase) {
|
||||||
|
this.apiBase = this.normalizeBase(newBase);
|
||||||
|
this.apiUrl = this.computeApiUrl(this.apiBase);
|
||||||
|
return this.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
setManagementKey(key) {
|
||||||
|
this.managementKey = key || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async request(endpoint, options = {}) {
|
||||||
|
const url = `${this.apiUrl}${endpoint}`;
|
||||||
|
const headers = {
|
||||||
|
'Authorization': `Bearer ${this.managementKey}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof this.onVersionUpdate === 'function') {
|
||||||
|
this.onVersionUpdate(response.headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(errorData.error || `HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/core/config-service.js
Normal file
70
src/core/config-service.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 配置缓存服务:负责分段/全量读取配置与缓存控制,不涉及任何 DOM
|
||||||
|
export class ConfigService {
|
||||||
|
constructor({ apiClient, cacheExpiry }) {
|
||||||
|
this.apiClient = apiClient;
|
||||||
|
this.cacheExpiry = cacheExpiry;
|
||||||
|
this.cache = {};
|
||||||
|
this.cacheTimestamps = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
isCacheValid(section = null) {
|
||||||
|
if (section) {
|
||||||
|
if (!(section in this.cache) || !(section in this.cacheTimestamps)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (Date.now() - this.cacheTimestamps[section]) < this.cacheExpiry;
|
||||||
|
}
|
||||||
|
if (!this.cache['__full__'] || !this.cacheTimestamps['__full__']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (Date.now() - this.cacheTimestamps['__full__']) < this.cacheExpiry;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache(section = null) {
|
||||||
|
if (section) {
|
||||||
|
delete this.cache[section];
|
||||||
|
delete this.cacheTimestamps[section];
|
||||||
|
if (this.cache['__full__']) {
|
||||||
|
delete this.cache['__full__'][section];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.keys(this.cache).forEach(key => delete this.cache[key]);
|
||||||
|
Object.keys(this.cacheTimestamps).forEach(key => delete this.cacheTimestamps[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConfig(section = null, forceRefresh = false) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (section && !forceRefresh && this.isCacheValid(section)) {
|
||||||
|
return this.cache[section];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!section && !forceRefresh && this.isCacheValid()) {
|
||||||
|
return this.cache['__full__'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await this.apiClient.request('/config');
|
||||||
|
|
||||||
|
if (section) {
|
||||||
|
this.cache[section] = config[section];
|
||||||
|
this.cacheTimestamps[section] = now;
|
||||||
|
if (this.cache['__full__']) {
|
||||||
|
this.cache['__full__'][section] = config[section];
|
||||||
|
} else {
|
||||||
|
this.cache['__full__'] = config;
|
||||||
|
this.cacheTimestamps['__full__'] = now;
|
||||||
|
}
|
||||||
|
return config[section];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache['__full__'] = config;
|
||||||
|
this.cacheTimestamps['__full__'] = now;
|
||||||
|
Object.keys(config).forEach(key => {
|
||||||
|
this.cache[key] = config[key];
|
||||||
|
this.cacheTimestamps[key] = now;
|
||||||
|
});
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,33 +7,31 @@ import { secureStorage } from '../utils/secure-storage.js';
|
|||||||
export const connectionModule = {
|
export const connectionModule = {
|
||||||
// 规范化基础地址,移除尾部斜杠与 /v0/management
|
// 规范化基础地址,移除尾部斜杠与 /v0/management
|
||||||
normalizeBase(input) {
|
normalizeBase(input) {
|
||||||
let base = (input || '').trim();
|
return this.apiClient.normalizeBase(input);
|
||||||
if (!base) return '';
|
|
||||||
// 若用户粘贴了完整地址,剥离后缀
|
|
||||||
base = base.replace(/\/?v0\/management\/?$/i, '');
|
|
||||||
base = base.replace(/\/+$/i, '');
|
|
||||||
// 自动补 http://
|
|
||||||
if (!/^https?:\/\//i.test(base)) {
|
|
||||||
base = 'http://' + base;
|
|
||||||
}
|
|
||||||
return base;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 由基础地址生成完整管理 API 地址
|
// 由基础地址生成完整管理 API 地址
|
||||||
computeApiUrl(base) {
|
computeApiUrl(base) {
|
||||||
const b = this.normalizeBase(base);
|
return this.apiClient.computeApiUrl(base);
|
||||||
if (!b) return '';
|
|
||||||
return b.replace(/\/$/, '') + '/v0/management';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setApiBase(newBase) {
|
setApiBase(newBase) {
|
||||||
this.apiBase = this.normalizeBase(newBase);
|
this.apiClient.setApiBase(newBase);
|
||||||
this.apiUrl = this.computeApiUrl(this.apiBase);
|
this.apiBase = this.apiClient.apiBase;
|
||||||
|
this.apiUrl = this.apiClient.apiUrl;
|
||||||
secureStorage.setItem('apiBase', this.apiBase);
|
secureStorage.setItem('apiBase', this.apiBase);
|
||||||
secureStorage.setItem('apiUrl', this.apiUrl); // 兼容旧字段
|
secureStorage.setItem('apiUrl', this.apiUrl); // 兼容旧字段
|
||||||
this.updateLoginConnectionInfo();
|
this.updateLoginConnectionInfo();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setManagementKey(key, { persist = true } = {}) {
|
||||||
|
this.managementKey = key || '';
|
||||||
|
this.apiClient.setManagementKey(this.managementKey);
|
||||||
|
if (persist) {
|
||||||
|
secureStorage.setItem('managementKey', this.managementKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 加载设置(简化版,仅加载内部状态)
|
// 加载设置(简化版,仅加载内部状态)
|
||||||
loadSettings() {
|
loadSettings() {
|
||||||
secureStorage.migratePlaintextKeys(['apiBase', 'apiUrl', 'managementKey']);
|
secureStorage.migratePlaintextKeys(['apiBase', 'apiUrl', 'managementKey']);
|
||||||
@@ -51,9 +49,7 @@ export const connectionModule = {
|
|||||||
this.setApiBase(this.detectApiBaseFromLocation());
|
this.setApiBase(this.detectApiBaseFromLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedKey) {
|
this.setManagementKey(savedKey || '', { persist: false });
|
||||||
this.managementKey = savedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateLoginConnectionInfo();
|
this.updateLoginConnectionInfo();
|
||||||
},
|
},
|
||||||
@@ -149,27 +145,8 @@ export const connectionModule = {
|
|||||||
|
|
||||||
// API 请求方法
|
// API 请求方法
|
||||||
async makeRequest(endpoint, options = {}) {
|
async makeRequest(endpoint, options = {}) {
|
||||||
const url = `${this.apiUrl}${endpoint}`;
|
|
||||||
const headers = {
|
|
||||||
'Authorization': `Bearer ${this.managementKey}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...options.headers
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
return await this.apiClient.request(endpoint, options);
|
||||||
...options,
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
|
|
||||||
this.updateVersionFromHeaders(response.headers);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json().catch(() => ({}));
|
|
||||||
throw new Error(errorData.error || `HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API请求失败:', error);
|
console.error('API请求失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -236,6 +213,13 @@ export const connectionModule = {
|
|||||||
|
|
||||||
// 更新连接信息显示
|
// 更新连接信息显示
|
||||||
this.updateConnectionInfo();
|
this.updateConnectionInfo();
|
||||||
|
|
||||||
|
if (this.events && typeof this.events.emit === 'function') {
|
||||||
|
this.events.emit('connection:status-changed', {
|
||||||
|
isConnected: this.isConnected,
|
||||||
|
apiBase: this.apiBase
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 检查连接状态
|
// 检查连接状态
|
||||||
@@ -270,66 +254,15 @@ export const connectionModule = {
|
|||||||
|
|
||||||
// 检查缓存是否有效
|
// 检查缓存是否有效
|
||||||
isCacheValid(section = null) {
|
isCacheValid(section = null) {
|
||||||
if (section) {
|
return this.configService.isCacheValid(section);
|
||||||
// 检查特定配置段的缓存
|
|
||||||
// 注意:配置值可能是 false、0、'' 等 falsy 值,不能用 ! 判断
|
|
||||||
if (!(section in this.configCache) || !(section in this.cacheTimestamps)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (Date.now() - this.cacheTimestamps[section]) < this.cacheExpiry;
|
|
||||||
}
|
|
||||||
// 检查全局缓存(兼容旧代码)
|
|
||||||
if (!this.configCache['__full__'] || !this.cacheTimestamps['__full__']) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (Date.now() - this.cacheTimestamps['__full__']) < this.cacheExpiry;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取配置(优先使用缓存,支持按段获取)
|
// 获取配置(优先使用缓存,支持按段获取)
|
||||||
async getConfig(section = null, forceRefresh = false) {
|
async getConfig(section = null, forceRefresh = false) {
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
// 如果请求特定配置段且该段缓存有效
|
|
||||||
if (section && !forceRefresh && this.isCacheValid(section)) {
|
|
||||||
this.updateConnectionStatus();
|
|
||||||
return this.configCache[section];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果请求全部配置且全局缓存有效
|
|
||||||
if (!section && !forceRefresh && this.isCacheValid()) {
|
|
||||||
this.updateConnectionStatus();
|
|
||||||
return this.configCache['__full__'];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const config = await this.makeRequest('/config');
|
const config = await this.configService.getConfig(section, forceRefresh);
|
||||||
|
this.configCache = this.configService.cache;
|
||||||
if (section) {
|
this.cacheTimestamps = this.configService.cacheTimestamps;
|
||||||
// 缓存特定配置段
|
|
||||||
this.configCache[section] = config[section];
|
|
||||||
this.cacheTimestamps[section] = now;
|
|
||||||
// 同时更新全局缓存中的这一段
|
|
||||||
if (this.configCache['__full__']) {
|
|
||||||
this.configCache['__full__'][section] = config[section];
|
|
||||||
} else {
|
|
||||||
// 如果全局缓存不存在,也创建它
|
|
||||||
this.configCache['__full__'] = config;
|
|
||||||
this.cacheTimestamps['__full__'] = now;
|
|
||||||
}
|
|
||||||
this.updateConnectionStatus();
|
|
||||||
return config[section];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 缓存全部配置
|
|
||||||
this.configCache['__full__'] = config;
|
|
||||||
this.cacheTimestamps['__full__'] = now;
|
|
||||||
|
|
||||||
// 同时缓存各个配置段
|
|
||||||
Object.keys(config).forEach(key => {
|
|
||||||
this.configCache[key] = config[key];
|
|
||||||
this.cacheTimestamps[key] = now;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.updateConnectionStatus();
|
this.updateConnectionStatus();
|
||||||
return config;
|
return config;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -340,18 +273,10 @@ export const connectionModule = {
|
|||||||
|
|
||||||
// 清除缓存(支持清除特定配置段)
|
// 清除缓存(支持清除特定配置段)
|
||||||
clearCache(section = null) {
|
clearCache(section = null) {
|
||||||
if (section) {
|
this.configService.clearCache(section);
|
||||||
// 清除特定配置段的缓存
|
this.configCache = this.configService.cache;
|
||||||
delete this.configCache[section];
|
this.cacheTimestamps = this.configService.cacheTimestamps;
|
||||||
delete this.cacheTimestamps[section];
|
if (!section) {
|
||||||
// 同时清除全局缓存中的这一段
|
|
||||||
if (this.configCache['__full__']) {
|
|
||||||
delete this.configCache['__full__'][section];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 清除所有缓存
|
|
||||||
this.configCache = {};
|
|
||||||
this.cacheTimestamps = {};
|
|
||||||
this.configYamlCache = '';
|
this.configYamlCache = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -410,6 +335,14 @@ export const connectionModule = {
|
|||||||
await this.loadConfigFileEditor(forceRefresh);
|
await this.loadConfigFileEditor(forceRefresh);
|
||||||
this.refreshConfigEditor();
|
this.refreshConfigEditor();
|
||||||
|
|
||||||
|
if (this.events && typeof this.events.emit === 'function') {
|
||||||
|
this.events.emit('data:config-loaded', {
|
||||||
|
config,
|
||||||
|
usageData,
|
||||||
|
keyStats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
console.log('配置加载完成,使用缓存:', !forceRefresh && this.isCacheValid());
|
console.log('配置加载完成,使用缓存:', !forceRefresh && this.isCacheValid());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载配置失败:', error);
|
console.error('加载配置失败:', error);
|
||||||
|
|||||||
10
src/core/event-bus.js
Normal file
10
src/core/event-bus.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// 轻量事件总线,避免模块之间的直接耦合
|
||||||
|
export function createEventBus() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
|
||||||
|
const on = (type, listener) => target.addEventListener(type, listener);
|
||||||
|
const off = (type, listener) => target.removeEventListener(type, listener);
|
||||||
|
const emit = (type, detail = {}) => target.dispatchEvent(new CustomEvent(type, { detail }));
|
||||||
|
|
||||||
|
return { on, off, emit };
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ export const loginModule = {
|
|||||||
async attemptAutoLogin(apiBase, managementKey) {
|
async attemptAutoLogin(apiBase, managementKey) {
|
||||||
try {
|
try {
|
||||||
this.setApiBase(apiBase);
|
this.setApiBase(apiBase);
|
||||||
this.managementKey = managementKey;
|
this.setManagementKey(managementKey);
|
||||||
|
|
||||||
const savedProxy = localStorage.getItem('proxyUrl');
|
const savedProxy = localStorage.getItem('proxyUrl');
|
||||||
if (savedProxy) {
|
if (savedProxy) {
|
||||||
@@ -79,8 +79,7 @@ export const loginModule = {
|
|||||||
async login(apiBase, managementKey) {
|
async login(apiBase, managementKey) {
|
||||||
try {
|
try {
|
||||||
this.setApiBase(apiBase);
|
this.setApiBase(apiBase);
|
||||||
this.managementKey = managementKey;
|
this.setManagementKey(managementKey);
|
||||||
secureStorage.setItem('managementKey', this.managementKey);
|
|
||||||
|
|
||||||
await this.testConnection();
|
await this.testConnection();
|
||||||
|
|
||||||
@@ -101,6 +100,7 @@ export const loginModule = {
|
|||||||
this.clearCache();
|
this.clearCache();
|
||||||
this.stopStatusUpdateTimer();
|
this.stopStatusUpdateTimer();
|
||||||
this.resetVersionInfo();
|
this.resetVersionInfo();
|
||||||
|
this.setManagementKey('', { persist: false });
|
||||||
|
|
||||||
localStorage.removeItem('isLoggedIn');
|
localStorage.removeItem('isLoggedIn');
|
||||||
secureStorage.removeItem('managementKey');
|
secureStorage.removeItem('managementKey');
|
||||||
@@ -132,8 +132,7 @@ export const loginModule = {
|
|||||||
}
|
}
|
||||||
this.hideLoginError();
|
this.hideLoginError();
|
||||||
|
|
||||||
this.managementKey = managementKey;
|
this.setManagementKey(managementKey);
|
||||||
secureStorage.setItem('managementKey', this.managementKey);
|
|
||||||
|
|
||||||
await this.login(this.apiBase, this.managementKey);
|
await this.login(this.apiBase, this.managementKey);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -210,6 +209,7 @@ export const loginModule = {
|
|||||||
if (loginKeyInput && savedKey) {
|
if (loginKeyInput && savedKey) {
|
||||||
loginKeyInput.value = savedKey;
|
loginKeyInput.value = savedKey;
|
||||||
}
|
}
|
||||||
|
this.setManagementKey(savedKey || '', { persist: false });
|
||||||
|
|
||||||
this.setupLoginAutoSave();
|
this.setupLoginAutoSave();
|
||||||
},
|
},
|
||||||
@@ -220,9 +220,9 @@ export const loginModule = {
|
|||||||
const resetButton = document.getElementById('login-reset-api-base');
|
const resetButton = document.getElementById('login-reset-api-base');
|
||||||
|
|
||||||
const saveKey = (val) => {
|
const saveKey = (val) => {
|
||||||
if (val.trim()) {
|
const trimmed = val.trim();
|
||||||
this.managementKey = val;
|
if (trimmed) {
|
||||||
secureStorage.setItem('managementKey', this.managementKey);
|
this.setManagementKey(trimmed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const saveKeyDebounced = this.debounce(saveKey, 500);
|
const saveKeyDebounced = this.debounce(saveKey, 500);
|
||||||
|
|||||||
Reference in New Issue
Block a user