增加求片功能
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getStorage } from '@/lib/db';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
// GET: 获取单个求片详情
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const storage = getStorage();
|
||||
const movieRequest = await storage.getMovieRequest(params.id);
|
||||
|
||||
if (!movieRequest) {
|
||||
return NextResponse.json({ error: '求片不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ request: movieRequest });
|
||||
} catch (error) {
|
||||
console.error('获取求片详情失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH: 更新求片状态(标记已上架)
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const storage = getStorage();
|
||||
const userInfo = await storage.getUserInfoV2(authInfo.username);
|
||||
|
||||
// 检查权限:只有管理员和站长可以操作
|
||||
if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') {
|
||||
return NextResponse.json({ error: '无权限操作' }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { status, fulfilledSource, fulfilledId } = body;
|
||||
|
||||
const movieRequest = await storage.getMovieRequest(params.id);
|
||||
if (!movieRequest) {
|
||||
return NextResponse.json({ error: '求片不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
const updates: any = {
|
||||
status,
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
|
||||
if (status === 'fulfilled') {
|
||||
updates.fulfilledAt = Date.now();
|
||||
updates.fulfilledSource = fulfilledSource;
|
||||
updates.fulfilledId = fulfilledId;
|
||||
|
||||
// 给所有求片用户发送通知
|
||||
for (const username of movieRequest.requestedBy) {
|
||||
await storage.addNotification(username, {
|
||||
id: `req_fulfilled_${params.id}_${Date.now()}`,
|
||||
type: 'request_fulfilled',
|
||||
title: '求片已上架',
|
||||
message: `您求的《${movieRequest.title}》已上架`,
|
||||
timestamp: Date.now(),
|
||||
read: false,
|
||||
metadata: {
|
||||
requestId: params.id,
|
||||
source: fulfilledSource,
|
||||
id: fulfilledId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await storage.updateMovieRequest(params.id, updates);
|
||||
|
||||
return NextResponse.json({
|
||||
message: '更新成功',
|
||||
request: { ...movieRequest, ...updates },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('更新求片失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: 删除求片
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { id: string } }
|
||||
) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const storage = getStorage();
|
||||
const userInfo = await storage.getUserInfoV2(authInfo.username);
|
||||
|
||||
// 检查权限:只有管理员和站长可以删除
|
||||
if (userInfo?.role !== 'admin' && userInfo?.role !== 'owner') {
|
||||
return NextResponse.json({ error: '无权限操作' }, { status: 403 });
|
||||
}
|
||||
|
||||
const movieRequest = await storage.getMovieRequest(params.id);
|
||||
if (!movieRequest) {
|
||||
return NextResponse.json({ error: '求片不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 删除求片
|
||||
await storage.deleteMovieRequest(params.id);
|
||||
|
||||
// 从所有用户的求片列表中移除
|
||||
for (const username of movieRequest.requestedBy) {
|
||||
await storage.removeUserMovieRequest(username, params.id);
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: '删除成功' });
|
||||
} catch (error) {
|
||||
console.error('删除求片失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
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 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 userInfo = await storage.getUserInfoV2(authInfo.username);
|
||||
const rateLimit = parseInt(process.env.MOVIE_REQUEST_RATE_LIMIT || '3600') * 1000;
|
||||
|
||||
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);
|
||||
|
||||
// 更新频率限制
|
||||
await storage.setGlobalValue(
|
||||
`user:${authInfo.username}:info:last_movie_request_time`,
|
||||
Date.now().toString()
|
||||
);
|
||||
|
||||
// 给站长发送通知
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user