feat: 百度网盘登录修正为可转存的方法&redis全局处理

This commit is contained in:
mazxd
2025-05-14 22:50:30 +08:00
parent 3ae21ac4c2
commit 38a5c69a37
5 changed files with 160 additions and 67 deletions
+15 -9
View File
@@ -116,11 +116,8 @@ export class BaiduDriveAdapter implements DriveAdapter {
try {
// 1. 尝试登录
await this.ensureLoggedIn(account);
// 2. 验证登录状态
await this.executeCommand(['pwd']);
// 3. 更新账号状态为活跃
// 2. 更新账号状态为活跃
await this.updateAccountStatus(account, 'active');
return true;
} catch (error: any) {
@@ -133,14 +130,23 @@ export class BaiduDriveAdapter implements DriveAdapter {
private async ensureLoggedIn(account: Account): Promise<void> {
try {
// 先尝试执行一个命令来检查是否已登录
await this.executeCommand(['pwd']);
// 检查登录状态
const whoResult = await this.executeCommand(['who']);
// 如果 uid 为 0 或用户名为空,说明未登录
if (whoResult.includes('uid: 0') || whoResult.includes('用户名: ,')) {
// 执行登录
await this.executeCommand([
'login',
`-bduss=${account.accountIdentifier}`,
`-stoken=${account.credentials}`
]);
}
} catch (error) {
// 如果未登录,则执行登录
// 如果命令执行失败,也尝试登录
await this.executeCommand([
'login',
account.accountIdentifier,
account.credentials
`-bduss=${account.accountIdentifier}`,
`-stoken=${account.credentials}`
]);
}
}
+5 -4
View File
@@ -4,14 +4,15 @@ import rateLimit from 'express-rate-limit'
import swaggerUi from 'swagger-ui-express'
import { swaggerSpec } from './config/swagger'
import { getSearchResults, getLinkForResource } from './controllers/searchController'
import { initRedis } from './lib/redis'
import { RedisService } from './services/redisService'
import logger from './lib/logger'
const app = express()
// 初始化 Redis 连接
initRedis().catch(err => {
logger.error('Redis 初始化失败,应用启动终止', { error: err })
// 初始化 Redis 服务
const redisService = RedisService.getInstance();
redisService.initialize().catch(err => {
logger.error('Redis 服务初始化失败,应用启动终止', { error: err })
process.exit(1)
})
-29
View File
@@ -1,29 +0,0 @@
import { createClient } from 'redis';
import { config } from '../config/config';
import logger from './logger';
// 创建 Redis 客户端实例
const redis = createClient({
url: `redis://${config.redis.host}:${config.redis.port}`,
password: config.redis.password
});
// Redis 错误处理和连接监控
redis.on('error', err => logger.error('Redis 客户端错误:', { error: err }));
redis.on('connect', () => logger.info('Redis 客户端已连接'));
redis.on('ready', () => logger.info('Redis 客户端就绪'));
redis.on('reconnecting', () => logger.warn('Redis 客户端正在重连'));
// 初始化 Redis 连接
export async function initRedis(): Promise<void> {
try {
await redis.connect();
logger.info('Redis 连接初始化成功');
} catch (err) {
logger.error('Redis 连接初始化失败:', { error: err });
throw err;
}
}
// 导出 Redis 客户端实例
export { redis };
+108
View File
@@ -0,0 +1,108 @@
import { createClient, RedisClientType } from 'redis';
import { config } from '../config/config';
import logger from '../lib/logger';
export class RedisService {
private static instance: RedisService;
private client: RedisClientType;
private isInitialized: boolean = false;
private constructor() {
this.client = createClient({
url: `redis://${config.redis.host}:${config.redis.port}`,
password: config.redis.password,
socket: {
reconnectStrategy: (retries) => {
if (retries > 10) {
logger.error('Redis 重连次数过多,停止重连');
return new Error('Redis 重连失败');
}
return Math.min(retries * 100, 3000);
}
}
});
this.setupEventListeners();
}
private setupEventListeners() {
this.client.on('error', err => {
logger.error('Redis 客户端错误:', { error: err });
});
this.client.on('connect', () => {
logger.info('Redis 客户端已连接', {
host: config.redis.host,
port: config.redis.port
});
});
this.client.on('ready', () => {
logger.info('Redis 客户端就绪');
});
this.client.on('reconnecting', () => {
logger.warn('Redis 客户端正在重连');
});
this.client.on('end', () => {
logger.warn('Redis 连接已关闭');
});
}
public static getInstance(): RedisService {
if (!RedisService.instance) {
RedisService.instance = new RedisService();
}
return RedisService.instance;
}
public async initialize(): Promise<void> {
if (!this.isInitialized) {
try {
await this.client.connect();
this.isInitialized = true;
logger.info('Redis 服务初始化成功');
} catch (err) {
logger.error('Redis 服务初始化失败:', { error: err });
throw err;
}
}
}
public async get(key: string): Promise<string | null> {
if (!this.isInitialized) {
await this.initialize();
}
return this.client.get(key);
}
public async set(key: string, value: string, options?: { EX?: number }): Promise<void> {
if (!this.isInitialized) {
await this.initialize();
}
await this.client.set(key, value, options);
}
public async del(key: string): Promise<void> {
if (!this.isInitialized) {
await this.initialize();
}
await this.client.del(key);
}
public async exists(key: string): Promise<boolean> {
if (!this.isInitialized) {
await this.initialize();
}
const result = await this.client.exists(key);
return result === 1;
}
public async quit(): Promise<void> {
if (this.isInitialized) {
await this.client.quit();
this.isInitialized = false;
}
}
}
+32 -25
View File
@@ -5,45 +5,52 @@ import { handleError } from '../decorators/errorHandler';
import { AppError } from '../utils/AppError';
import { LoginResponse, SearchResponse, SearchParams, SearchResult, LinkResult } from '../entity/search.types';
import { SEARCH_SERVICE } from '../constants/redis.keys';
import { redis } from '../lib/redis';
import { RedisService } from './redisService';
export class SearchService {
private baseUrl: string;
private redisService: RedisService;
constructor() {
this.baseUrl = config.searchService.baseUrl;
this.redisService = RedisService.getInstance();
logger.info('搜索服务已初始化', { baseUrl: this.baseUrl });
}
@handleError('获取认证 token 失败')
private async getToken(): Promise<string> {
// 尝试从 Redis 获取 token
const cachedToken = await redis.get(SEARCH_SERVICE.TOKEN);
if (cachedToken) {
logger.debug('从缓存中获取到 token');
return cachedToken;
}
logger.info('缓存中未找到 token,尝试登录获取');
// 登录获取新 token
const response = await axios.post<LoginResponse>(
`${this.baseUrl}${config.searchService.endpoints.login}`,
{
username: process.env.SEARCH_SERVICE_USERNAME,
password: process.env.SEARCH_SERVICE_PASSWORD,
try {
// 尝试从 Redis 获取 token
const cachedToken = await this.redisService.get(SEARCH_SERVICE.TOKEN);
if (cachedToken) {
logger.debug('从缓存中获取到 token');
return cachedToken;
}
);
if (!response.data.success || !response.data.data.token) {
throw new AppError('登录失败', 401);
logger.info('缓存中未找到 token,尝试登录获取');
// 登录获取新 token
const response = await axios.post<LoginResponse>(
`${this.baseUrl}${config.searchService.endpoints.login}`,
{
username: process.env.SEARCH_SERVICE_USERNAME,
password: process.env.SEARCH_SERVICE_PASSWORD,
}
);
if (!response.data.success || !response.data.data.token) {
throw new AppError('登录失败', 401);
}
logger.info('成功获取新 token');
// 将 token 缓存到 Redis
await this.redisService.set(SEARCH_SERVICE.TOKEN, response.data.data.token, {
EX: 21600, // 6 小时
});
return response.data.data.token;
} catch (error) {
logger.error('获取 token 失败:', { error });
throw error;
}
logger.info('成功获取新 token');
// 将 token 缓存到 Redis
await redis.set(SEARCH_SERVICE.TOKEN, response.data.data.token, {
EX: 21600, // 6 小时
});
return response.data.data.token;
}
@handleError('搜索执行失败')