mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-23 03:04:43 +08:00
fix: search history isolation
This commit is contained in:
@@ -10,12 +10,22 @@ export const runtime = 'edge';
|
||||
const HISTORY_LIMIT = 20;
|
||||
|
||||
/**
|
||||
* GET /api/searchhistory
|
||||
* GET /api/searchhistory?user=<username>
|
||||
* 返回 string[]
|
||||
*/
|
||||
export async function GET() {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const history = await db.getSearchHistory();
|
||||
const { searchParams } = new URL(request.url);
|
||||
const user = searchParams.get('user')?.trim();
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const history = await db.getSearchHistory(user);
|
||||
return NextResponse.json(history, { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('获取搜索历史失败', err);
|
||||
@@ -28,12 +38,14 @@ export async function GET() {
|
||||
|
||||
/**
|
||||
* POST /api/searchhistory
|
||||
* body: { keyword: string }
|
||||
* body: { keyword: string, user: string }
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const keyword: string = body.keyword?.trim();
|
||||
const user: string = body.user?.trim();
|
||||
|
||||
if (!keyword) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Keyword is required' },
|
||||
@@ -41,10 +53,17 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
await db.addSearchHistory(keyword);
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
await db.addSearchHistory(user, keyword);
|
||||
|
||||
// 再次获取最新列表,确保客户端与服务端同步
|
||||
const history = await db.getSearchHistory();
|
||||
const history = await db.getSearchHistory(user);
|
||||
return NextResponse.json(history.slice(0, HISTORY_LIMIT), { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('添加搜索历史失败', err);
|
||||
@@ -56,7 +75,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/searchhistory
|
||||
* DELETE /api/searchhistory?user=<username>&keyword=<kw>
|
||||
*
|
||||
* 1. 不带 keyword -> 清空全部搜索历史
|
||||
* 2. 带 keyword=<kw> -> 删除单条关键字
|
||||
@@ -64,9 +83,17 @@ export async function POST(request: NextRequest) {
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const user = searchParams.get('user')?.trim();
|
||||
const kw = searchParams.get('keyword')?.trim();
|
||||
|
||||
await db.deleteSearchHistory(kw || undefined);
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
await db.deleteSearchHistory(user, kw || undefined);
|
||||
|
||||
return NextResponse.json({ success: true }, { status: 200 });
|
||||
} catch (err) {
|
||||
|
||||
@@ -206,7 +206,10 @@ export async function getSearchHistory(): Promise<string[]> {
|
||||
// 如果配置为使用数据库,则从后端 API 获取
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
return fetchFromApi<string[]>('/api/searchhistory');
|
||||
const user = getUsername();
|
||||
return fetchFromApi<string[]>(
|
||||
`/api/searchhistory?user=${encodeURIComponent(user ?? '')}`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('获取搜索历史失败:', err);
|
||||
return [];
|
||||
@@ -240,12 +243,13 @@ export async function addSearchHistory(keyword: string): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
await fetch('/api/searchhistory', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ keyword: trimmed }),
|
||||
body: JSON.stringify({ keyword: trimmed, user: user ?? '' }),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('保存搜索历史失败:', err);
|
||||
@@ -276,7 +280,8 @@ export async function clearSearchHistory(): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
await fetch('/api/searchhistory', {
|
||||
const user = getUsername();
|
||||
await fetch(`/api/searchhistory?user=${encodeURIComponent(user ?? '')}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -300,9 +305,15 @@ export async function deleteSearchHistory(keyword: string): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
await fetch(`/api/searchhistory?keyword=${encodeURIComponent(trimmed)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const user = getUsername();
|
||||
await fetch(
|
||||
`/api/searchhistory?user=${encodeURIComponent(
|
||||
user ?? ''
|
||||
)}&keyword=${encodeURIComponent(trimmed)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('删除搜索历史失败:', err);
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ export interface IStorage {
|
||||
checkUserExist(userName: string): Promise<boolean>;
|
||||
|
||||
// 搜索历史相关
|
||||
getSearchHistory(): Promise<string[]>;
|
||||
addSearchHistory(keyword: string): Promise<void>;
|
||||
deleteSearchHistory(keyword?: string): Promise<void>;
|
||||
getSearchHistory(userName: string): Promise<string[]>;
|
||||
addSearchHistory(userName: string, keyword: string): Promise<void>;
|
||||
deleteSearchHistory(userName: string, keyword?: string): Promise<void>;
|
||||
|
||||
// 用户列表
|
||||
getAllUsers(): Promise<string[]>;
|
||||
@@ -184,26 +184,30 @@ class RedisStorage implements IStorage {
|
||||
}
|
||||
|
||||
// ---------- 搜索历史 ----------
|
||||
private shKey = 'moontv:search_history';
|
||||
|
||||
async getSearchHistory(): Promise<string[]> {
|
||||
return (await this.client.lRange(this.shKey, 0, -1)) as string[];
|
||||
private shKey(user: string) {
|
||||
return `u:${user}:sh`; // u:username:sh
|
||||
}
|
||||
|
||||
async addSearchHistory(keyword: string): Promise<void> {
|
||||
async getSearchHistory(userName: string): Promise<string[]> {
|
||||
return (await this.client.lRange(this.shKey(userName), 0, -1)) as string[];
|
||||
}
|
||||
|
||||
async addSearchHistory(userName: string, keyword: string): Promise<void> {
|
||||
const key = this.shKey(userName);
|
||||
// 先去重
|
||||
await this.client.lRem(this.shKey, 0, keyword);
|
||||
await this.client.lRem(key, 0, keyword);
|
||||
// 插入到最前
|
||||
await this.client.lPush(this.shKey, keyword);
|
||||
await this.client.lPush(key, keyword);
|
||||
// 限制最大长度
|
||||
await this.client.lTrim(this.shKey, 0, SEARCH_HISTORY_LIMIT - 1);
|
||||
await this.client.lTrim(key, 0, SEARCH_HISTORY_LIMIT - 1);
|
||||
}
|
||||
|
||||
async deleteSearchHistory(keyword?: string): Promise<void> {
|
||||
async deleteSearchHistory(userName: string, keyword?: string): Promise<void> {
|
||||
const key = this.shKey(userName);
|
||||
if (keyword) {
|
||||
await this.client.lRem(this.shKey, 0, keyword);
|
||||
await this.client.lRem(key, 0, keyword);
|
||||
} else {
|
||||
await this.client.del(this.shKey);
|
||||
await this.client.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,16 +375,16 @@ export class DbManager {
|
||||
}
|
||||
|
||||
// ---------- 搜索历史 ----------
|
||||
async getSearchHistory(): Promise<string[]> {
|
||||
return this.storage.getSearchHistory();
|
||||
async getSearchHistory(userName: string): Promise<string[]> {
|
||||
return this.storage.getSearchHistory(userName);
|
||||
}
|
||||
|
||||
async addSearchHistory(keyword: string): Promise<void> {
|
||||
await this.storage.addSearchHistory(keyword);
|
||||
async addSearchHistory(userName: string, keyword: string): Promise<void> {
|
||||
await this.storage.addSearchHistory(userName, keyword);
|
||||
}
|
||||
|
||||
async deleteSearchHistory(keyword?: string): Promise<void> {
|
||||
await this.storage.deleteSearchHistory(keyword);
|
||||
async deleteSearchHistory(userName: string, keyword?: string): Promise<void> {
|
||||
await this.storage.deleteSearchHistory(userName, keyword);
|
||||
}
|
||||
|
||||
// 获取全部用户名
|
||||
|
||||
Reference in New Issue
Block a user