feat: Add Docker Compose configurations for Kvrocks and Redis deployments

- Implemented `docker-compose.kvrocks.auth.yml` for Kvrocks with password authentication.
- Created `docker-compose.redis.yml` for Redis deployment.
- Added Kvrocks configuration file `kvrocks.auth.conf` with necessary settings.
- Updated documentation with deployment guidelines for Kvrocks.
- Introduced ESLint configuration for code quality.
- Developed deployment configuration check script `check-deployment-configs.js`.
- Added D1 database initialization script `d1-init.sql` for KatelyaTV.
- Created test script `test-kvrocks-deployment.js` to validate Kvrocks deployment.
- Implemented fix verification script `verify-kvrocks-fix.js` for password handling.
- Updated `wrangler.toml` for Cloudflare deployment configuration.
This commit is contained in:
katelya
2025-09-04 17:55:23 +08:00
parent 63120d418b
commit 82485d1939
20 changed files with 1943 additions and 154 deletions
+17
View File
@@ -0,0 +1,17 @@
module.exports = {
env: {
node: true,
es6: true,
},
extends: ['eslint:recommended'],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
rules: {
'no-console': 'off', // 允许在脚本中使用 console
'no-unused-vars': 'off', // 暂时忽略未使用变量
'@typescript-eslint/no-var-requires': 'off', // 允许 require
'import/no-import-module-exports': 'off',
},
};
+301
View File
@@ -0,0 +1,301 @@
#!/usr/bin/env node
/**
* KatelyaTV 全方案部署状态检查脚本
* 检查所有部署方案的配置文件和环境是否完整
*/
const fs = require('fs');
const path = require('path');
console.log('🔍 KatelyaTV 部署配置检查开始...\n');
// 检查结果统计
let checkResults = {
total: 0,
passed: 0,
failed: 0,
warnings: 0,
errors: []
};
// 辅助函数
function logCheck(name, status, message = '') {
checkResults.total++;
if (status === 'PASS') {
checkResults.passed++;
console.log(`${name}: PASS ${message}`);
} else if (status === 'WARN') {
checkResults.warnings++;
console.log(`⚠️ ${name}: WARN ${message}`);
} else {
checkResults.failed++;
console.log(`${name}: FAIL ${message}`);
checkResults.errors.push(`${name}: ${message}`);
}
}
function fileExists(filePath) {
try {
return fs.existsSync(filePath);
} catch (error) {
return false;
}
}
function readJsonFile(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content);
} catch (error) {
return null;
}
}
// 检查1Docker 部署配置
function checkDockerConfigs() {
console.log('🐳 检查 Docker 部署配置...');
const dockerConfigs = [
{
name: 'Docker + Redis 配置',
files: ['docker-compose.redis.yml', '.env.redis.example']
},
{
name: 'Docker + Kvrocks 配置(无密码)',
files: ['docker-compose.kvrocks.yml', '.env.kvrocks.example']
},
{
name: 'Docker + Kvrocks 配置(密码认证)',
files: ['docker-compose.kvrocks.auth.yml']
},
{
name: 'Docker + Kvrocks 本地构建配置',
files: ['docker-compose.kvrocks.local.yml']
}
];
for (const config of dockerConfigs) {
let allFilesExist = true;
let missingFiles = [];
for (const file of config.files) {
if (!fileExists(file)) {
allFilesExist = false;
missingFiles.push(file);
}
}
if (allFilesExist) {
logCheck(config.name, 'PASS', '所有配置文件存在');
} else {
logCheck(config.name, 'FAIL', `缺失文件: ${missingFiles.join(', ')}`);
}
}
}
// 检查2Cloudflare 部署配置
function checkCloudflareConfigs() {
console.log('\n☁️ 检查 Cloudflare 部署配置...');
const cloudflareFiles = [
'wrangler.toml',
'.env.cloudflare.example',
'scripts/d1-init.sql'
];
for (const file of cloudflareFiles) {
if (fileExists(file)) {
logCheck(`Cloudflare 配置文件 ${file}`, 'PASS', '文件存在');
} else {
logCheck(`Cloudflare 配置文件 ${file}`, 'FAIL', '文件不存在');
}
}
// 检查 wrangler.toml 内容
if (fileExists('wrangler.toml')) {
const content = fs.readFileSync('wrangler.toml', 'utf8');
if (content.includes('d1_databases') && content.includes('pages:build')) {
logCheck('wrangler.toml 内容', 'PASS', '包含必要配置');
} else {
logCheck('wrangler.toml 内容', 'WARN', '可能缺少部分配置');
}
}
}
// 检查3Vercel 部署配置
function checkVercelConfigs() {
console.log('\n▲ 检查 Vercel 部署配置...');
const vercelFile = 'vercel.json';
if (fileExists(vercelFile)) {
logCheck('Vercel 配置文件', 'PASS', 'vercel.json 存在');
const vercelConfig = readJsonFile(vercelFile);
if (vercelConfig) {
if (vercelConfig.build && vercelConfig.build.env) {
logCheck('Vercel 构建配置', 'PASS', '包含环境变量配置');
} else {
logCheck('Vercel 构建配置', 'WARN', '可能缺少构建环境配置');
}
}
} else {
logCheck('Vercel 配置文件', 'FAIL', 'vercel.json 不存在');
}
}
// 检查4:环境变量示例文件
function checkEnvExamples() {
console.log('\n⚙️ 检查环境变量示例文件...');
const envFiles = [
'.env.example',
'.env.redis.example',
'.env.kvrocks.example',
'.env.cloudflare.example'
];
for (const envFile of envFiles) {
if (fileExists(envFile)) {
const content = fs.readFileSync(envFile, 'utf8');
const hasStorageType = content.includes('NEXT_PUBLIC_STORAGE_TYPE');
const hasAuthConfig = content.includes('NEXTAUTH_SECRET');
if (hasStorageType && hasAuthConfig) {
logCheck(`环境变量文件 ${envFile}`, 'PASS', '包含必要配置');
} else {
logCheck(`环境变量文件 ${envFile}`, 'WARN', '可能缺少部分配置');
}
} else {
logCheck(`环境变量文件 ${envFile}`, 'FAIL', '文件不存在');
}
}
}
// 检查5package.json 脚本
function checkPackageScripts() {
console.log('\n📦 检查 package.json 构建脚本...');
const packageJson = readJsonFile('package.json');
if (packageJson && packageJson.scripts) {
const requiredScripts = [
'dev',
'build',
'start',
'pages:build', // Cloudflare Pages
'lint'
];
for (const script of requiredScripts) {
if (packageJson.scripts[script]) {
logCheck(`package.json 脚本 ${script}`, 'PASS', '脚本存在');
} else {
logCheck(`package.json 脚本 ${script}`, 'WARN', '脚本不存在或未配置');
}
}
} else {
logCheck('package.json', 'FAIL', '文件不存在或格式错误');
}
}
// 检查6Kvrocks 配置文件
function checkKvrocksConfigs() {
console.log('\n🏪 检查 Kvrocks 配置文件...');
const kvrocksConfigs = [
'docker/kvrocks/kvrocks.conf',
'docker/kvrocks/kvrocks.auth.conf'
];
for (const configFile of kvrocksConfigs) {
if (fileExists(configFile)) {
const content = fs.readFileSync(configFile, 'utf8');
const hasBasicConfig = content.includes('bind') && content.includes('port');
if (hasBasicConfig) {
logCheck(`Kvrocks 配置 ${path.basename(configFile)}`, 'PASS', '包含基本配置');
} else {
logCheck(`Kvrocks 配置 ${path.basename(configFile)}`, 'WARN', '可能缺少基本配置');
}
} else {
logCheck(`Kvrocks 配置 ${path.basename(configFile)}`, 'FAIL', '文件不存在');
}
}
}
// 检查7:文档文件
function checkDocumentation() {
console.log('\n📚 检查文档文件...');
const docFiles = [
'README.md',
'docs/KVROCKS.md',
'docs/KVROCKS_DEPLOYMENT.md',
'docs/TVBOX.md',
'KVROCKS_FIX_REPORT.md'
];
for (const docFile of docFiles) {
if (fileExists(docFile)) {
logCheck(`文档文件 ${docFile}`, 'PASS', '文件存在');
} else {
logCheck(`文档文件 ${docFile}`, 'WARN', '文件不存在');
}
}
}
// 主检查函数
async function runChecks() {
try {
await checkDockerConfigs();
await checkCloudflareConfigs();
await checkVercelConfigs();
await checkEnvExamples();
await checkPackageScripts();
await checkKvrocksConfigs();
await checkDocumentation();
} catch (error) {
console.error('检查执行出错:', error);
checkResults.failed++;
checkResults.errors.push(`检查执行出错: ${error.message}`);
}
// 输出检查结果
console.log('\n' + '='.repeat(60));
console.log('📊 部署配置检查结果汇总:');
console.log(` 总计: ${checkResults.total} 项检查`);
console.log(` 通过: ${checkResults.passed} 项 ✅`);
console.log(` 警告: ${checkResults.warnings} 项 ⚠️`);
console.log(` 失败: ${checkResults.failed} 项 ❌`);
if (checkResults.failed > 0) {
console.log('\n🚨 失败的检查项:');
checkResults.errors.forEach((error, index) => {
console.log(` ${index + 1}. ${error}`);
});
}
if (checkResults.warnings > 0) {
console.log('\n⚠️ 警告说明:');
console.log(' - 警告项目不影响基本功能,但建议完善');
console.log(' - 可能影响特定部署方案或高级功能');
}
if (checkResults.failed === 0) {
console.log('\n🎉 所有必要配置文件检查通过!');
console.log(' 您可以选择以下任意部署方案:');
console.log(' 1. 🐳 Docker + Redis (docker-compose.redis.yml)');
console.log(' 2. 🏪 Docker + Kvrocks (docker-compose.kvrocks.yml)');
console.log(' 3. ☁️ Cloudflare Pages + D1 (wrangler.toml)');
console.log(' 4. ▲ Vercel + Upstash (vercel.json)');
}
console.log('='.repeat(60));
// 退出代码
process.exit(checkResults.failed > 0 ? 1 : 0);
}
// 运行检查
runChecks().catch(console.error);
+109
View File
@@ -0,0 +1,109 @@
-- D1 数据库初始化脚本
-- 用于创建 KatelyaTV 所需的数据表
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 播放记录表
CREATE TABLE IF NOT EXISTS play_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
record_key TEXT NOT NULL,
video_url TEXT,
current_time REAL DEFAULT 0,
duration REAL DEFAULT 0,
episode_index INTEGER DEFAULT 0,
episode_url TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (user_id, record_key)
);
-- 收藏表
CREATE TABLE IF NOT EXISTS favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
favorite_key TEXT NOT NULL,
title TEXT,
cover_url TEXT,
rating REAL,
year TEXT,
area TEXT,
category TEXT,
actors TEXT,
director TEXT,
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (user_id, favorite_key)
);
-- 搜索历史表
CREATE TABLE IF NOT EXISTS search_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
keyword TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 跳过配置表
CREATE TABLE IF NOT EXISTS skip_configs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
config_key TEXT NOT NULL,
start_time INTEGER DEFAULT 0,
end_time INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (user_id, config_key)
);
-- 管理员配置表
CREATE TABLE IF NOT EXISTS admin_configs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
config_key TEXT UNIQUE NOT NULL,
config_value TEXT,
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 插入默认管理员配置
INSERT OR IGNORE INTO admin_configs (config_key, config_value, description) VALUES
('site_name', 'KatelyaTV', '站点名称'),
('site_description', '高性能影视播放平台', '站点描述'),
('enable_register', 'true', '是否允许用户注册'),
('max_users', '100', '最大用户数量'),
('cache_ttl', '3600', '缓存时间(秒)');
-- 创建索引以提高查询性能
CREATE INDEX IF NOT EXISTS idx_play_records_user_id ON play_records(user_id);
CREATE INDEX IF NOT EXISTS idx_play_records_record_key ON play_records(record_key);
CREATE INDEX IF NOT EXISTS idx_favorites_user_id ON favorites(user_id);
CREATE INDEX IF NOT EXISTS idx_search_history_user_id ON search_history(user_id);
CREATE INDEX IF NOT EXISTS idx_skip_configs_user_id ON skip_configs(user_id);
-- 创建视图以简化查询
CREATE VIEW IF NOT EXISTS user_stats AS
SELECT
u.id,
u.username,
COUNT(DISTINCT pr.id) as play_count,
COUNT(DISTINCT f.id) as favorite_count,
COUNT(DISTINCT sh.id) as search_count,
u.created_at
FROM users u
LEFT JOIN play_records pr ON u.id = pr.user_id
LEFT JOIN favorites f ON u.id = f.user_id
LEFT JOIN search_history sh ON u.id = sh.user_id
GROUP BY u.id, u.username, u.created_at;
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env node
/**
* Kvrocks 部署测试脚本
* 用于验证 Docker + Kvrocks 部署是否正常工作
*/
const { createClient } = require('redis');
const { spawn } = require('child_process');
const fs = require('fs');
// 配置
const TEST_CONFIG = {
KVROCKS_URL: process.env.KVROCKS_URL || 'redis://localhost:6666',
KVROCKS_PASSWORD: process.env.KVROCKS_PASSWORD,
KVROCKS_DATABASE: parseInt(process.env.KVROCKS_DATABASE || '0'),
TEST_TIMEOUT: 30000, // 30秒超时
};
console.log('🧪 Kvrocks 部署测试开始...\n');
// 测试结果统计
let testResults = {
total: 0,
passed: 0,
failed: 0,
errors: []
};
// 辅助函数
function logTest(name, status, message = '') {
testResults.total++;
if (status === 'PASS') {
testResults.passed++;
console.log(`${name}: PASS ${message}`);
} else {
testResults.failed++;
console.log(`${name}: FAIL ${message}`);
testResults.errors.push(`${name}: ${message}`);
}
}
// 测试1:检查 Docker Compose 文件
async function testDockerComposeFiles() {
console.log('📁 测试 Docker Compose 配置文件...');
const files = [
'docker-compose.kvrocks.yml',
'docker-compose.kvrocks.auth.yml'
];
for (const file of files) {
try {
if (fs.existsSync(file)) {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('kvrocks:') && content.includes('katelyatv:')) {
logTest(`Docker Compose 文件 ${file}`, 'PASS', '配置正确');
} else {
logTest(`Docker Compose 文件 ${file}`, 'FAIL', '配置缺失');
}
} else {
logTest(`Docker Compose 文件 ${file}`, 'FAIL', '文件不存在');
}
} catch (error) {
logTest(`Docker Compose 文件 ${file}`, 'FAIL', error.message);
}
}
}
// 测试2:检查环境变量配置
async function testEnvironmentConfig() {
console.log('\n🔧 测试环境变量配置...');
// 检查必需的环境变量
const requiredVars = ['NEXT_PUBLIC_STORAGE_TYPE'];
const optionalVars = ['KVROCKS_PASSWORD', 'NEXTAUTH_SECRET'];
for (const varName of requiredVars) {
if (process.env[varName]) {
logTest(`环境变量 ${varName}`, 'PASS', `值: ${process.env[varName]}`);
} else {
logTest(`环境变量 ${varName}`, 'FAIL', '未设置');
}
}
for (const varName of optionalVars) {
if (process.env[varName]) {
logTest(`环境变量 ${varName}`, 'PASS', '已设置');
} else {
logTest(`环境变量 ${varName}`, 'PASS', '未设置(可选)');
}
}
// 检查存储类型
if (process.env.NEXT_PUBLIC_STORAGE_TYPE === 'kvrocks') {
logTest('存储类型配置', 'PASS', 'kvrocks');
} else {
logTest('存储类型配置', 'FAIL', `期望 kvrocks,实际 ${process.env.NEXT_PUBLIC_STORAGE_TYPE}`);
}
}
// 测试3Kvrocks 连接测试
async function testKvrocksConnection() {
console.log('\n🔌 测试 Kvrocks 连接...');
let client;
try {
// 构建客户端配置
const clientConfig = {
url: TEST_CONFIG.KVROCKS_URL,
database: TEST_CONFIG.KVROCKS_DATABASE,
socket: {
connectTimeout: 5000,
},
};
// 只有当密码存在且不为空时才添加密码配置
if (TEST_CONFIG.KVROCKS_PASSWORD && TEST_CONFIG.KVROCKS_PASSWORD.trim() !== '') {
clientConfig.password = TEST_CONFIG.KVROCKS_PASSWORD;
console.log('🔐 使用密码认证连接');
} else {
console.log('🔓 无密码认证连接');
}
client = createClient(clientConfig);
// 连接
await client.connect();
logTest('Kvrocks 连接', 'PASS', '连接成功');
// 测试 PING
const pong = await client.ping();
if (pong === 'PONG') {
logTest('Kvrocks PING', 'PASS', 'PONG');
} else {
logTest('Kvrocks PING', 'FAIL', `响应: ${pong}`);
}
// 测试基本操作
const testKey = 'test:' + Date.now();
const testValue = 'test-value-' + Math.random();
await client.set(testKey, testValue);
const getValue = await client.get(testKey);
if (getValue === testValue) {
logTest('Kvrocks 读写操作', 'PASS', '数据一致');
} else {
logTest('Kvrocks 读写操作', 'FAIL', `期望 ${testValue},实际 ${getValue}`);
}
// 清理测试数据
await client.del(testKey);
// 测试数据库信息
const info = await client.info();
if (info.includes('kvrocks_version')) {
const version = info.match(/kvrocks_version:([^\r\n]+)/)?.[1];
logTest('Kvrocks 版本信息', 'PASS', `版本: ${version}`);
} else {
logTest('Kvrocks 版本信息', 'FAIL', '无法获取版本信息');
}
} catch (error) {
logTest('Kvrocks 连接', 'FAIL', error.message);
} finally {
if (client && client.isOpen) {
await client.quit();
}
}
}
// 测试4Docker 服务状态检查
async function testDockerServices() {
console.log('\n🐳 测试 Docker 服务状态...');
return new Promise((resolve) => {
const docker = spawn('docker-compose', ['ps'], { stdio: 'pipe' });
let output = '';
docker.stdout.on('data', (data) => {
output += data.toString();
});
docker.on('close', (code) => {
if (code === 0) {
if (output.includes('kvrocks') && output.includes('Up')) {
logTest('Docker Kvrocks 服务', 'PASS', '服务运行中');
} else {
logTest('Docker Kvrocks 服务', 'FAIL', '服务未运行');
}
if (output.includes('katelyatv') && output.includes('Up')) {
logTest('Docker KatelyaTV 服务', 'PASS', '服务运行中');
} else {
logTest('Docker KatelyaTV 服务', 'FAIL', '服务未运行或未启动');
}
} else {
logTest('Docker 服务检查', 'FAIL', 'docker-compose 命令执行失败');
}
resolve();
});
docker.on('error', (error) => {
logTest('Docker 服务检查', 'FAIL', `Docker 未安装或不可用: ${error.message}`);
resolve();
});
});
}
// 主测试函数
async function runTests() {
console.log(`🏗️ 测试配置:`);
console.log(` Kvrocks URL: ${TEST_CONFIG.KVROCKS_URL}`);
console.log(` 密码认证: ${TEST_CONFIG.KVROCKS_PASSWORD ? '是' : '否'}`);
console.log(` 数据库: ${TEST_CONFIG.KVROCKS_DATABASE}`);
console.log('');
try {
await testDockerComposeFiles();
await testEnvironmentConfig();
await testDockerServices();
await testKvrocksConnection();
} catch (error) {
console.error('测试执行出错:', error);
testResults.failed++;
testResults.errors.push(`测试执行出错: ${error.message}`);
}
// 输出测试结果
console.log('\n' + '='.repeat(50));
console.log('📊 测试结果汇总:');
console.log(` 总计: ${testResults.total} 项测试`);
console.log(` 通过: ${testResults.passed} 项 ✅`);
console.log(` 失败: ${testResults.failed} 项 ❌`);
if (testResults.failed > 0) {
console.log('\n🚨 失败的测试项:');
testResults.errors.forEach((error, index) => {
console.log(` ${index + 1}. ${error}`);
});
console.log('\n💡 解决建议:');
console.log(' 1. 检查 Docker 服务是否正常启动');
console.log(' 2. 验证环境变量配置是否正确');
console.log(' 3. 确认网络连接是否正常');
console.log(' 4. 查看详细部署指南: docs/KVROCKS_DEPLOYMENT.md');
} else {
console.log('\n🎉 所有测试通过!Kvrocks 部署正常工作。');
}
console.log('='.repeat(50));
// 退出代码
process.exit(testResults.failed > 0 ? 1 : 0);
}
// 运行测试
runTests().catch(console.error);
+122
View File
@@ -0,0 +1,122 @@
/* eslint-disable no-console, @typescript-eslint/no-explicit-any */
/**
* 验证 Kvrocks 密码处理修复
* 模拟用户反馈的错误场景
*/
// 模拟用户的环境变量设置
process.env.NEXT_PUBLIC_STORAGE_TYPE = 'kvrocks';
process.env.KVROCKS_URL = 'redis://kvrocks:6666';
process.env.KVROCKS_PASSWORD = ''; // 用户设置了空密码,这是问题所在
process.env.KVROCKS_DATABASE = '0';
// 模拟 Redis 客户端创建函数
function createClient(config) {
console.log('🔧 创建 Redis 客户端配置:', JSON.stringify(config, null, 2));
if (config.password === '') {
console.log('❌ 检测到空密码,这会导致认证错误!');
return {
connect: () => Promise.reject(new Error('ERR Client sent AUTH, but no password is set')),
isOpen: false
};
} else if (config.password === undefined) {
console.log('✅ 无密码配置,正常连接');
return {
connect: () => Promise.resolve(),
isOpen: true
};
} else {
console.log('✅ 有效密码配置,正常连接');
return {
connect: () => Promise.resolve(),
isOpen: true
};
}
}
// 使用修复后的客户端创建逻辑
function getKvrocksClient() {
const kvrocksUrl = process.env.KVROCKS_URL || 'redis://localhost:6666';
const kvrocksPassword = process.env.KVROCKS_PASSWORD;
const kvrocksDatabase = parseInt(process.env.KVROCKS_DATABASE || '0');
console.log('🏪 Initializing Kvrocks client...');
console.log('🔗 Kvrocks URL:', kvrocksUrl);
console.log('🔑 Password configured:', kvrocksPassword ? 'Yes' : 'No');
console.log('🔑 Password value:', JSON.stringify(kvrocksPassword));
// 构建客户端配置
const clientConfig = {
url: kvrocksUrl,
database: kvrocksDatabase,
socket: {
connectTimeout: 10000,
},
};
// 只有当密码存在且不为空时才添加密码配置
if (kvrocksPassword && kvrocksPassword.trim() !== '') {
clientConfig.password = kvrocksPassword;
console.log('🔐 Using password authentication');
} else {
console.log('🔓 No password authentication (connecting without password)');
}
return createClient(clientConfig);
}
async function testScenarios() {
console.log('🧪 测试不同密码配置场景\n');
// 场景1:用户的问题场景 - 空字符串密码
console.log('📝 场景1:用户问题场景(空字符串密码)');
console.log('环境变量: KVROCKS_PASSWORD=""');
process.env.KVROCKS_PASSWORD = '';
try {
const client = getKvrocksClient();
await client.connect();
console.log('✅ 场景1通过:无认证错误\n');
} catch (error) {
console.log('❌ 场景1失败:', error.message, '\n');
}
// 场景2:未设置密码
console.log('📝 场景2:未设置密码');
console.log('环境变量: KVROCKS_PASSWORD=undefined');
delete process.env.KVROCKS_PASSWORD;
try {
const client = getKvrocksClient();
await client.connect();
console.log('✅ 场景2通过:无认证错误\n');
} catch (error) {
console.log('❌ 场景2失败:', error.message, '\n');
}
// 场景3:有效密码
console.log('📝 场景3:有效密码');
console.log('环境变量: KVROCKS_PASSWORD="validpassword"');
process.env.KVROCKS_PASSWORD = 'validpassword';
try {
const client = getKvrocksClient();
await client.connect();
console.log('✅ 场景3通过:正常密码认证\n');
} catch (error) {
console.log('❌ 场景3失败:', error.message, '\n');
}
// 场景4:只有空格的密码
console.log('📝 场景4:只有空格的密码');
console.log('环境变量: KVROCKS_PASSWORD=" "');
process.env.KVROCKS_PASSWORD = ' ';
try {
const client = getKvrocksClient();
await client.connect();
console.log('✅ 场景4通过:空格密码被正确处理\n');
} catch (error) {
console.log('❌ 场景4失败:', error.message, '\n');
}
}
testScenarios().catch(console.error);