添加Valkey支持
This commit is contained in:
@@ -117,11 +117,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="init-header" style="margin: 24px 0 16px 0; text-align: left;">
|
||||||
|
<h3 style="font-size: 16px; color: var(--text-main); margin: 0;">Valkey / Redis 缓存配置 (可选)</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group" style="flex: 2;">
|
||||||
|
<label for="vHost">Valkey 地址</label>
|
||||||
|
<input type="text" id="vHost" value="localhost" placeholder="localhost" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="flex: 1;">
|
||||||
|
<label for="vPort">端口</label>
|
||||||
|
<input type="number" id="vPort" value="6379" placeholder="6379" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group form-group-wide">
|
||||||
|
<label for="vPassword">Valkey 密码</label>
|
||||||
|
<input type="password" id="vPassword" placeholder="留空则无密码">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-message" id="messageBox"></div>
|
<div class="form-message" id="messageBox"></div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions" style="flex-wrap: wrap;">
|
||||||
<button class="btn btn-test" id="btnTest">测试连接</button>
|
<button class="btn btn-test" id="btnTest" style="flex: 1 1 45%;">测试 MySQL</button>
|
||||||
<button class="btn btn-add" id="btnInit">初始化数据库</button>
|
<button class="btn btn-test" id="btnTestValkey" style="flex: 1 1 45%;">测试 Valkey</button>
|
||||||
|
<button class="btn btn-add" id="btnInit" style="flex: 1 1 100%;">确认并初始化系统</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const userInput = document.getElementById('user');
|
const userInput = document.getElementById('user');
|
||||||
const passwordInput = document.getElementById('password');
|
const passwordInput = document.getElementById('password');
|
||||||
const databaseInput = document.getElementById('database');
|
const databaseInput = document.getElementById('database');
|
||||||
|
const vHostInput = document.getElementById('vHost');
|
||||||
|
const vPortInput = document.getElementById('vPort');
|
||||||
|
const vPasswordInput = document.getElementById('vPassword');
|
||||||
|
|
||||||
const btnTest = document.getElementById('btnTest');
|
const btnTest = document.getElementById('btnTest');
|
||||||
|
const btnTestValkey = document.getElementById('btnTestValkey');
|
||||||
const btnInit = document.getElementById('btnInit');
|
const btnInit = document.getElementById('btnInit');
|
||||||
const messageBox = document.getElementById('messageBox');
|
const messageBox = document.getElementById('messageBox');
|
||||||
|
|
||||||
@@ -102,6 +106,34 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
btnTestValkey.addEventListener('click', async () => {
|
||||||
|
btnTestValkey.disabled = true;
|
||||||
|
const oldText = btnTestValkey.textContent;
|
||||||
|
btnTestValkey.textContent = '测试中...';
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/setup/test-valkey', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
host: vHostInput.value,
|
||||||
|
port: vPortInput.value,
|
||||||
|
password: vPasswordInput.value
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.success) {
|
||||||
|
showMessage('Valkey 连接成功!');
|
||||||
|
} else {
|
||||||
|
showMessage('Valkey 连接失败: ' + (data.error || '未知错误'), true);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showMessage('Valkey 请求失败: ' + err.message, true);
|
||||||
|
} finally {
|
||||||
|
btnTestValkey.disabled = false;
|
||||||
|
btnTestValkey.textContent = oldText;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
btnInit.addEventListener('click', async () => {
|
btnInit.addEventListener('click', async () => {
|
||||||
btnInit.disabled = true;
|
btnInit.disabled = true;
|
||||||
const oldText = btnInit.textContent;
|
const oldText = btnInit.textContent;
|
||||||
@@ -115,7 +147,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
port: portInput.value,
|
port: portInput.value,
|
||||||
user: userInput.value,
|
user: userInput.value,
|
||||||
password: passwordInput.value,
|
password: passwordInput.value,
|
||||||
database: databaseInput.value
|
database: databaseInput.value,
|
||||||
|
vHost: vHostInput.value,
|
||||||
|
vPort: vPortInput.value,
|
||||||
|
vPassword: vPasswordInput.value
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
const Redis = require('ioredis');
|
const Redis = require('ioredis');
|
||||||
|
|
||||||
const host = process.env.VALKEY_HOST || 'localhost';
|
|
||||||
const port = parseInt(process.env.VALKEY_PORT) || 6379;
|
|
||||||
const password = process.env.VALKEY_PASSWORD || undefined;
|
|
||||||
const db = parseInt(process.env.VALKEY_DB) || 0;
|
|
||||||
const ttl = parseInt(process.env.VALKEY_TTL) || 30;
|
|
||||||
|
|
||||||
let redis = null;
|
let redis = null;
|
||||||
|
let ttl = 30;
|
||||||
|
|
||||||
try {
|
function init() {
|
||||||
|
if (redis) {
|
||||||
|
redis.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = process.env.VALKEY_HOST || 'localhost';
|
||||||
|
const port = parseInt(process.env.VALKEY_PORT) || 6379;
|
||||||
|
const password = process.env.VALKEY_PASSWORD || undefined;
|
||||||
|
const db = parseInt(process.env.VALKEY_DB) || 0;
|
||||||
|
ttl = parseInt(process.env.VALKEY_TTL) || 30;
|
||||||
|
|
||||||
|
try {
|
||||||
redis = new Redis({
|
redis = new Redis({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
@@ -22,11 +28,15 @@ try {
|
|||||||
// Fail silently after one retry, we just won't cache
|
// Fail silently after one retry, we just won't cache
|
||||||
console.warn('[Cache] Valkey connection failed, caching disabled:', err.message);
|
console.warn('[Cache] Valkey connection failed, caching disabled:', err.message);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[Cache] Valkey init failed:', err.message);
|
console.warn('[Cache] Valkey init failed:', err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
const cache = {
|
const cache = {
|
||||||
|
init,
|
||||||
async get(key) {
|
async get(key) {
|
||||||
if (!redis) return null;
|
if (!redis) return null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -137,8 +137,29 @@ app.post('/api/setup/test', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/setup/test-valkey', async (req, res) => {
|
||||||
|
const { host, port, password } = req.body;
|
||||||
|
try {
|
||||||
|
const Redis = require('ioredis');
|
||||||
|
const redis = new Redis({
|
||||||
|
host: host || 'localhost',
|
||||||
|
port: parseInt(port) || 6379,
|
||||||
|
password: password || undefined,
|
||||||
|
lazyConnect: true,
|
||||||
|
maxRetriesPerRequest: 1,
|
||||||
|
connectTimeout: 5000
|
||||||
|
});
|
||||||
|
await redis.connect();
|
||||||
|
await redis.ping();
|
||||||
|
await redis.disconnect();
|
||||||
|
res.json({ success: true, message: 'Valkey connection successful' });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(400).json({ success: false, error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/setup/init', async (req, res) => {
|
app.post('/api/setup/init', async (req, res) => {
|
||||||
const { host, port, user, password, database } = req.body;
|
const { host, port, user, password, database, vHost, vPort, vPassword } = req.body;
|
||||||
try {
|
try {
|
||||||
const mysql = require('mysql2/promise');
|
const mysql = require('mysql2/promise');
|
||||||
const connection = await mysql.createConnection({
|
const connection = await mysql.createConnection({
|
||||||
@@ -211,6 +232,9 @@ MYSQL_PORT=${port || '3306'}
|
|||||||
MYSQL_USER=${user || 'root'}
|
MYSQL_USER=${user || 'root'}
|
||||||
MYSQL_PASSWORD=${password || ''}
|
MYSQL_PASSWORD=${password || ''}
|
||||||
MYSQL_DATABASE=${dbName}
|
MYSQL_DATABASE=${dbName}
|
||||||
|
VALKEY_HOST=${vHost || 'localhost'}
|
||||||
|
VALKEY_PORT=${vPort || '6379'}
|
||||||
|
VALKEY_PASSWORD=${vPassword || ''}
|
||||||
PORT=${process.env.PORT || 3000}
|
PORT=${process.env.PORT || 3000}
|
||||||
HOST=${process.env.HOST || '0.0.0.0'}
|
HOST=${process.env.HOST || '0.0.0.0'}
|
||||||
REFRESH_INTERVAL=${process.env.REFRESH_INTERVAL || 5000}
|
REFRESH_INTERVAL=${process.env.REFRESH_INTERVAL || 5000}
|
||||||
@@ -223,9 +247,13 @@ REFRESH_INTERVAL=${process.env.REFRESH_INTERVAL || 5000}
|
|||||||
process.env.MYSQL_USER = user;
|
process.env.MYSQL_USER = user;
|
||||||
process.env.MYSQL_PASSWORD = password;
|
process.env.MYSQL_PASSWORD = password;
|
||||||
process.env.MYSQL_DATABASE = dbName;
|
process.env.MYSQL_DATABASE = dbName;
|
||||||
|
process.env.VALKEY_HOST = vHost;
|
||||||
|
process.env.VALKEY_PORT = vPort;
|
||||||
|
process.env.VALKEY_PASSWORD = vPassword;
|
||||||
|
|
||||||
// Re-initialize pool
|
// Re-initialize pools
|
||||||
db.initPool();
|
db.initPool();
|
||||||
|
cache.init();
|
||||||
|
|
||||||
isDbInitialized = true;
|
isDbInitialized = true;
|
||||||
res.json({ success: true, message: 'Initialization complete' });
|
res.json({ success: true, message: 'Initialization complete' });
|
||||||
@@ -607,6 +635,10 @@ app.get('/api/metrics/overview', async (req, res) => {
|
|||||||
// Get network traffic history (past 24h) from Prometheus
|
// Get network traffic history (past 24h) from Prometheus
|
||||||
app.get('/api/metrics/network-history', async (req, res) => {
|
app.get('/api/metrics/network-history', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
const cacheKey = 'network_history_all';
|
||||||
|
const cached = await cache.get(cacheKey);
|
||||||
|
if (cached) return res.json(cached);
|
||||||
|
|
||||||
const [sources] = await db.query('SELECT * FROM prometheus_sources');
|
const [sources] = await db.query('SELECT * FROM prometheus_sources');
|
||||||
if (sources.length === 0) {
|
if (sources.length === 0) {
|
||||||
return res.json({ timestamps: [], rx: [], tx: [] });
|
return res.json({ timestamps: [], rx: [], tx: [] });
|
||||||
@@ -625,6 +657,7 @@ app.get('/api/metrics/network-history', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const merged = prometheusService.mergeNetworkHistories(validHistories);
|
const merged = prometheusService.mergeNetworkHistories(validHistories);
|
||||||
|
await cache.set(cacheKey, merged, 300); // Cache for 5 minutes
|
||||||
res.json(merged);
|
res.json(merged);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching network history history:', err);
|
console.error('Error fetching network history history:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user