Files
MoonTVPlus/src/app/api/movie-requests/route.ts
T
2026-01-12 21:06:45 +08:00

215 lines
6.7 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { nanoid } from 'nanoid';
import { getAuthInfoFromCookie } from '@/lib/auth';
import { getStorage } from '@/lib/db';
import { MovieRequest } from '@/lib/types';
export const runtime = 'nodejs';
// GET: 获取求片列表
export async function GET(request: NextRequest) {
const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const { searchParams } = new URL(request.url);
const status = searchParams.get('status') as 'pending' | 'fulfilled' | null;
const detail = searchParams.get('detail') !== 'false';
const myRequests = searchParams.get('my') === 'true';
const storage = getStorage();
if (myRequests) {
// 获取用户自己的求片
const requestIds = await storage.getUserMovieRequests(authInfo.username);
const requests = await Promise.all(
requestIds.map(id => storage.getMovieRequest(id))
);
const filtered = requests.filter(r => r !== null) as MovieRequest[];
return NextResponse.json({ requests: filtered });
}
// 获取所有求片
let requests = await storage.getAllMovieRequests();
// 按状态筛选
if (status) {
requests = requests.filter(r => r.status === status);
}
// 列表页不返回 requestedBy
if (!detail) {
requests = requests.map(r => ({ ...r, requestedBy: undefined }));
}
// 按求片人数和时间排序
requests.sort((a, b) => {
if (b.requestCount !== a.requestCount) {
return b.requestCount - a.requestCount;
}
return b.createdAt - a.createdAt;
});
return NextResponse.json({ requests });
} catch (error) {
console.error('获取求片列表失败:', error);
return NextResponse.json(
{ error: (error as Error).message },
{ status: 500 }
);
}
}
// POST: 创建或加入求片
export async function POST(request: NextRequest) {
const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
// 检查求片功能是否启用并获取冷却时间
const { getConfig } = await import('@/lib/config');
const config = await getConfig();
if (config.SiteConfig.EnableMovieRequest === false) {
return NextResponse.json({ error: '求片功能已关闭' }, { status: 403 });
}
const body = await request.json();
const { tmdbId, title, year, mediaType, season, poster, overview } = body;
if (!title || !mediaType) {
return NextResponse.json({ error: '缺少必要参数' }, { status: 400 });
}
const storage = getStorage();
// 检查频率限制 - 使用配置中的冷却时间
const cooldownSeconds = config.SiteConfig.MovieRequestCooldown ?? 3600;
const rateLimit = cooldownSeconds * 1000;
const userInfo = await storage.getUserInfoV2(authInfo.username);
if (userInfo?.last_movie_request_time) {
const elapsed = Date.now() - userInfo.last_movie_request_time;
if (elapsed < rateLimit) {
const remaining = Math.ceil((rateLimit - elapsed) / 60000);
return NextResponse.json(
{ error: `操作太频繁,请${remaining}分钟后再试` },
{ status: 429 }
);
}
}
// 查重(剧集需要匹配季度)
const allRequests = await storage.getAllMovieRequests();
const existing = allRequests.find(r =>
(tmdbId && r.tmdbId === tmdbId && r.season === season) ||
(r.title === title && r.year === year && r.season === season)
);
if (existing) {
// 如果已上架,不允许再求
if (existing.status === 'fulfilled') {
return NextResponse.json({ error: '该影片已上架' }, { status: 400 });
}
// 检查用户是否已经求过
if (existing.requestedBy.includes(authInfo.username)) {
return NextResponse.json({ error: '您已经求过这部影片了' }, { status: 400 });
}
// 加入求片
existing.requestedBy.push(authInfo.username);
existing.requestCount++;
existing.updatedAt = Date.now();
await storage.updateMovieRequest(existing.id, existing);
await storage.addUserMovieRequest(authInfo.username, existing.id);
// 给站长发送通知
const ownerUsername = process.env.USERNAME;
if (ownerUsername) {
await storage.addNotification(ownerUsername, {
id: `movie_request_join_${existing.id}_${Date.now()}`,
type: 'movie_request',
title: '求片人数增加',
message: `${authInfo.username} 也想看:${existing.title}${existing.season ? ` 第${existing.season}季` : ''} (${existing.requestCount}人)`,
timestamp: Date.now(),
read: false,
metadata: {
requestId: existing.id,
username: authInfo.username,
},
});
}
return NextResponse.json({
message: '已加入求片',
request: existing
});
}
// 创建新求片
const newRequest: MovieRequest = {
id: nanoid(),
tmdbId,
title,
year,
mediaType,
season,
poster,
overview,
requestedBy: [authInfo.username],
requestCount: 1,
status: 'pending',
createdAt: Date.now(),
updatedAt: Date.now(),
};
await storage.createMovieRequest(newRequest);
await storage.addUserMovieRequest(authInfo.username, newRequest.id);
// 更新频率限制 - 保存到用户信息的 hash 中
await storage.client.hSet(
`user:${authInfo.username}:info`,
'last_movie_request_time',
Date.now().toString()
);
// 清除用户信息缓存,确保下次读取到最新数据
const { userInfoCache } = await import('@/lib/user-cache');
userInfoCache?.delete(authInfo.username);
// 给站长发送通知
const ownerUsername = process.env.USERNAME;
if (ownerUsername) {
await storage.addNotification(ownerUsername, {
id: `movie_request_${newRequest.id}_${Date.now()}`,
type: 'movie_request',
title: '新求片请求',
message: `${authInfo.username} 求片:${title}${season ? ` 第${season}季` : ''}`,
timestamp: Date.now(),
read: false,
metadata: {
requestId: newRequest.id,
username: authInfo.username,
},
});
}
return NextResponse.json({
message: '求片成功',
request: newRequest
});
} catch (error) {
console.error('创建求片失败:', error);
return NextResponse.json(
{ error: (error as Error).message },
{ status: 500 }
);
}
}