添加Valkey支持

This commit is contained in:
CN-JS-HuiBai
2026-04-05 15:02:19 +08:00
parent 484a7a766f
commit e7b8000808
4 changed files with 128 additions and 27 deletions

View File

@@ -116,12 +116,35 @@
<input type="text" id="database" value="display_wall" autocomplete="off">
</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="actions">
<button class="btn btn-test" id="btnTest">测试连接</button>
<button class="btn btn-add" id="btnInit">初始化数据库</button>
<div class="actions" style="flex-wrap: wrap;">
<button class="btn btn-test" id="btnTest" style="flex: 1 1 45%;">测试 MySQL</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>

View File

@@ -4,8 +4,12 @@ document.addEventListener('DOMContentLoaded', () => {
const userInput = document.getElementById('user');
const passwordInput = document.getElementById('password');
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 btnTestValkey = document.getElementById('btnTestValkey');
const btnInit = document.getElementById('btnInit');
const messageBox = document.getElementById('messageBox');
@@ -101,6 +105,34 @@ document.addEventListener('DOMContentLoaded', () => {
btnTest.textContent = oldText;
}
});
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.disabled = true;
@@ -115,7 +147,10 @@ document.addEventListener('DOMContentLoaded', () => {
port: portInput.value,
user: userInput.value,
password: passwordInput.value,
database: databaseInput.value
database: databaseInput.value,
vHost: vHostInput.value,
vPort: vPortInput.value,
vPassword: vPasswordInput.value
})
});
const data = await res.json();

View File

@@ -1,32 +1,42 @@
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 ttl = 30;
try {
redis = new Redis({
host,
port,
password,
db,
lazyConnect: true,
maxRetriesPerRequest: 1
});
function init() {
if (redis) {
redis.disconnect();
}
redis.on('error', (err) => {
// Fail silently after one retry, we just won't cache
console.warn('[Cache] Valkey connection failed, caching disabled:', err.message);
});
} catch (err) {
console.warn('[Cache] Valkey init failed:', err.message);
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({
host,
port,
password,
db,
lazyConnect: true,
maxRetriesPerRequest: 1
});
redis.on('error', (err) => {
// Fail silently after one retry, we just won't cache
console.warn('[Cache] Valkey connection failed, caching disabled:', err.message);
});
} catch (err) {
console.warn('[Cache] Valkey init failed:', err.message);
}
}
init();
const cache = {
init,
async get(key) {
if (!redis) return null;
try {

View File

@@ -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) => {
const { host, port, user, password, database } = req.body;
const { host, port, user, password, database, vHost, vPort, vPassword } = req.body;
try {
const mysql = require('mysql2/promise');
const connection = await mysql.createConnection({
@@ -211,6 +232,9 @@ MYSQL_PORT=${port || '3306'}
MYSQL_USER=${user || 'root'}
MYSQL_PASSWORD=${password || ''}
MYSQL_DATABASE=${dbName}
VALKEY_HOST=${vHost || 'localhost'}
VALKEY_PORT=${vPort || '6379'}
VALKEY_PASSWORD=${vPassword || ''}
PORT=${process.env.PORT || 3000}
HOST=${process.env.HOST || '0.0.0.0'}
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_PASSWORD = password;
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();
cache.init();
isDbInitialized = true;
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
app.get('/api/metrics/network-history', async (req, res) => {
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');
if (sources.length === 0) {
return res.json({ timestamps: [], rx: [], tx: [] });
@@ -625,6 +657,7 @@ app.get('/api/metrics/network-history', async (req, res) => {
}
const merged = prometheusService.mergeNetworkHistories(validHistories);
await cache.set(cacheKey, merged, 300); // Cache for 5 minutes
res.json(merged);
} catch (err) {
console.error('Error fetching network history history:', err);