mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-22 02:24:44 +08:00
feat: admin api cookie
This commit is contained in:
@@ -27,6 +27,7 @@ import { Suspense, useCallback, useEffect, useState } from 'react';
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
import { AdminConfig, AdminConfigResult } from '@/lib/admin.types';
|
||||
import { getAuthInfoFromBrowserCookie } from '@/lib/auth';
|
||||
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
|
||||
@@ -118,8 +119,7 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => {
|
||||
});
|
||||
|
||||
// 当前登录用户名
|
||||
const currentUsername =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('username') : null;
|
||||
const currentUsername = getAuthInfoFromBrowserCookie()?.username || null;
|
||||
|
||||
useEffect(() => {
|
||||
if (config?.UserConfig) {
|
||||
@@ -131,15 +131,6 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => {
|
||||
|
||||
// 切换允许注册设置
|
||||
const toggleAllowRegister = async (value: boolean) => {
|
||||
const username =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('username') : null;
|
||||
const password =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('password') : null;
|
||||
if (!username || !password) {
|
||||
showError('无法获取当前用户信息,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 先更新本地 UI
|
||||
setUserSettings((prev) => ({ ...prev, enableRegistration: value }));
|
||||
@@ -148,8 +139,6 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
action: 'setAllowRegister',
|
||||
allowRegister: value,
|
||||
}),
|
||||
@@ -197,23 +186,11 @@ const UserConfig = ({ config, role, refreshConfig }: UserConfigProps) => {
|
||||
targetUsername: string,
|
||||
targetPassword?: string
|
||||
) => {
|
||||
const username =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('username') : null;
|
||||
const password =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('password') : null;
|
||||
|
||||
if (!username || !password) {
|
||||
showError('无法获取当前用户信息,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/admin/user', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
targetUsername,
|
||||
...(targetPassword ? { targetPassword } : {}),
|
||||
action,
|
||||
@@ -521,21 +498,11 @@ const VideoSourceConfig = ({
|
||||
|
||||
// 通用 API 请求
|
||||
const callSourceApi = async (body: Record<string, any>) => {
|
||||
const username =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('username') : null;
|
||||
const password =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('password') : null;
|
||||
|
||||
if (!username || !password) {
|
||||
showError('无法获取当前用户信息,请重新登录');
|
||||
throw new Error('no-credential');
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await fetch('/api/admin/source', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password, ...body }),
|
||||
body: JSON.stringify({ ...body }),
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
@@ -845,26 +812,12 @@ const SiteConfigComponent = ({ config }: { config: AdminConfig | null }) => {
|
||||
|
||||
// 保存站点配置
|
||||
const handleSave = async () => {
|
||||
const username =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('username') : null;
|
||||
if (!username) {
|
||||
showError('无法获取用户名,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
const password =
|
||||
typeof window !== 'undefined' ? localStorage.getItem('password') : null;
|
||||
if (!password) {
|
||||
showError('无法获取密码,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setSaving(true);
|
||||
const resp = await fetch('/api/admin/site', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password, ...siteSettings }),
|
||||
body: JSON.stringify({ ...siteSettings }),
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
@@ -1024,12 +977,7 @@ function AdminPageClient() {
|
||||
setLoading(true);
|
||||
}
|
||||
|
||||
const username = localStorage.getItem('username');
|
||||
const response = await fetch(
|
||||
`/api/admin/config${
|
||||
username ? `?username=${encodeURIComponent(username)}` : ''
|
||||
}`
|
||||
);
|
||||
const response = await fetch(`/api/admin/config`);
|
||||
|
||||
if (!response.ok) {
|
||||
const data = (await response.json()) as any;
|
||||
@@ -1065,11 +1013,6 @@ function AdminPageClient() {
|
||||
|
||||
// 新增: 重置配置处理函数
|
||||
const handleResetConfig = async () => {
|
||||
const username = localStorage.getItem('username');
|
||||
if (!username) {
|
||||
showError('无法获取用户名,请重新登录');
|
||||
return;
|
||||
}
|
||||
const { isConfirmed } = await Swal.fire({
|
||||
title: '确认重置配置',
|
||||
text: '此操作将重置用户封禁和管理员设置、自定义视频源,站点配置将重置为默认值,是否继续?',
|
||||
@@ -1080,22 +1023,8 @@ function AdminPageClient() {
|
||||
});
|
||||
if (!isConfirmed) return;
|
||||
|
||||
const password = localStorage.getItem('password');
|
||||
if (!password) {
|
||||
showError('无法获取密码,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/admin/reset${
|
||||
username
|
||||
? `?username=${encodeURIComponent(
|
||||
username
|
||||
)}&password=${encodeURIComponent(password)}`
|
||||
: ''
|
||||
}`
|
||||
);
|
||||
const response = await fetch(`/api/admin/reset`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`重置失败: ${response.status}`);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { AdminConfigResult } from '@/lib/admin.types';
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
export async function GET(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
@@ -18,8 +19,11 @@ export async function GET(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const username = searchParams.get('username');
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const username = authInfo.username;
|
||||
|
||||
try {
|
||||
const config = getConfig();
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { resetConfig } from '@/lib/config';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
export async function GET(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
@@ -17,18 +18,13 @@ export async function GET(request: Request) {
|
||||
);
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const username = searchParams.get('username');
|
||||
const password = searchParams.get('password');
|
||||
|
||||
if (!username || !password) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名和密码不能为空' },
|
||||
{ status: 400 }
|
||||
);
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const username = authInfo.username;
|
||||
|
||||
if (username !== process.env.USERNAME || password !== process.env.PASSWORD) {
|
||||
if (username !== process.env.USERNAME) {
|
||||
return NextResponse.json({ error: '仅支持站长重置配置' }, { status: 401 });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { getStorage } from '@/lib/db';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export async function POST(request: Request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
@@ -21,17 +22,19 @@ export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const username = authInfo.username;
|
||||
|
||||
const {
|
||||
username,
|
||||
password,
|
||||
SiteName,
|
||||
Announcement,
|
||||
SearchDownstreamMaxPage,
|
||||
SiteInterfaceCacheTime,
|
||||
SearchResultDefaultAggregate,
|
||||
} = body as {
|
||||
username?: string;
|
||||
password?: string;
|
||||
SiteName: string;
|
||||
Announcement: string;
|
||||
SearchDownstreamMaxPage: number;
|
||||
@@ -50,23 +53,11 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!username || !password) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名和密码不能为空' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const adminConfig = getConfig();
|
||||
const storage = getStorage();
|
||||
|
||||
// 权限与密码校验
|
||||
if (username === process.env.USERNAME) {
|
||||
// 站长
|
||||
if (password !== process.env.PASSWORD) {
|
||||
return NextResponse.json({ error: '密码错误' }, { status: 401 });
|
||||
}
|
||||
} else {
|
||||
// 权限校验
|
||||
if (username !== process.env.USERNAME) {
|
||||
// 管理员
|
||||
const user = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === username
|
||||
@@ -74,18 +65,6 @@ export async function POST(request: Request) {
|
||||
if (!user || user.role !== 'admin') {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!storage || typeof storage.verifyUser !== 'function') {
|
||||
return NextResponse.json(
|
||||
{ error: '存储未配置用户认证' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const ok = await storage.verifyUser(username, password);
|
||||
if (!ok) {
|
||||
return NextResponse.json({ error: '密码错误' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
// 更新缓存中的站点设置
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { getStorage } from '@/lib/db';
|
||||
import { IStorage } from '@/lib/types';
|
||||
@@ -12,12 +13,10 @@ export const runtime = 'edge';
|
||||
type Action = 'add' | 'disable' | 'enable' | 'delete' | 'sort';
|
||||
|
||||
interface BaseBody {
|
||||
username?: string;
|
||||
password?: string;
|
||||
action?: Action;
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
@@ -30,12 +29,17 @@ export async function POST(request: Request) {
|
||||
|
||||
try {
|
||||
const body = (await request.json()) as BaseBody & Record<string, any>;
|
||||
const { action } = body;
|
||||
|
||||
const { username, password, action } = body;
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const username = authInfo.username;
|
||||
|
||||
// 基础校验
|
||||
const ACTIONS: Action[] = ['add', 'disable', 'enable', 'delete', 'sort'];
|
||||
if (!username || !password || !action || !ACTIONS.includes(action)) {
|
||||
if (!username || !action || !ACTIONS.includes(action)) {
|
||||
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
|
||||
}
|
||||
|
||||
@@ -44,33 +48,13 @@ export async function POST(request: Request) {
|
||||
const storage: IStorage | null = getStorage();
|
||||
|
||||
// 权限与身份校验
|
||||
if (username === process.env.USERNAME) {
|
||||
if (password !== process.env.PASSWORD) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (username !== process.env.USERNAME) {
|
||||
const userEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === username
|
||||
);
|
||||
if (!userEntry || userEntry.role !== 'admin') {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
if (!storage || typeof storage.verifyUser !== 'function') {
|
||||
return NextResponse.json(
|
||||
{ error: '存储未配置用户认证' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
const pass = await storage.verifyUser(username, password);
|
||||
if (!pass) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,no-console,@typescript-eslint/no-non-null-assertion */
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { getStorage } from '@/lib/db';
|
||||
import { IStorage } from '@/lib/types';
|
||||
@@ -18,7 +19,7 @@ const ACTIONS = [
|
||||
'setAllowRegister',
|
||||
] as const;
|
||||
|
||||
export async function POST(request: Request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||
if (storageType === 'localstorage') {
|
||||
return NextResponse.json(
|
||||
@@ -32,23 +33,25 @@ export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const username = authInfo.username;
|
||||
|
||||
const {
|
||||
username, // 操作者用户名
|
||||
password, // 操作者密码
|
||||
targetUsername, // 目标用户名
|
||||
targetPassword, // 目标用户密码(仅在添加用户时需要)
|
||||
allowRegister,
|
||||
action,
|
||||
} = body as {
|
||||
username?: string;
|
||||
password?: string;
|
||||
targetUsername?: string;
|
||||
targetPassword?: string;
|
||||
allowRegister?: boolean;
|
||||
action?: (typeof ACTIONS)[number];
|
||||
};
|
||||
|
||||
if (!username || !password || !action || !ACTIONS.includes(action)) {
|
||||
if (!action || !ACTIONS.includes(action)) {
|
||||
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
|
||||
}
|
||||
|
||||
@@ -71,12 +74,6 @@ export async function POST(request: Request) {
|
||||
let operatorRole: 'owner' | 'admin';
|
||||
if (username === process.env.USERNAME) {
|
||||
operatorRole = 'owner';
|
||||
if (password !== process.env.PASSWORD) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const userEntry = adminConfig.UserConfig.Users.find(
|
||||
(u) => u.username === username
|
||||
@@ -85,20 +82,6 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||
}
|
||||
operatorRole = 'admin';
|
||||
|
||||
if (!storage || typeof storage.verifyUser !== 'function') {
|
||||
return NextResponse.json(
|
||||
{ error: '存储未配置用户认证' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
const ok = await storage.verifyUser(username, password);
|
||||
if (!ok) {
|
||||
return NextResponse.json(
|
||||
{ error: '用户名或密码错误' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 查找目标用户条目
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { db } from '@/lib/db';
|
||||
import { Favorite } from '@/lib/types';
|
||||
|
||||
@@ -16,13 +17,14 @@ export const runtime = 'edge';
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const key = searchParams.get('key');
|
||||
const user = searchParams.get('user');
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 查询单条收藏
|
||||
if (key) {
|
||||
@@ -33,12 +35,12 @@ export async function GET(request: NextRequest) {
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
const fav = await db.getFavorite(user, source, id);
|
||||
const fav = await db.getFavorite(authInfo.username, source, id);
|
||||
return NextResponse.json(fav, { status: 200 });
|
||||
}
|
||||
|
||||
// 查询全部收藏
|
||||
const favorites = await db.getAllFavorites(user);
|
||||
const favorites = await db.getAllFavorites(authInfo.username);
|
||||
return NextResponse.json(favorites, { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('获取收藏失败', err);
|
||||
@@ -51,21 +53,19 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
/**
|
||||
* POST /api/favorites
|
||||
* body: { user?: string; key: string; favorite: Favorite }
|
||||
* body: { key: string; favorite: Favorite }
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const {
|
||||
user,
|
||||
key,
|
||||
favorite,
|
||||
}: { user?: string; key: string; favorite: Favorite } = body;
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { key, favorite }: { key: string; favorite: Favorite } = body;
|
||||
|
||||
if (!key || !favorite) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing key or favorite' },
|
||||
@@ -94,7 +94,7 @@ export async function POST(request: NextRequest) {
|
||||
save_time: favorite.save_time ?? Date.now(),
|
||||
} as Omit<Favorite, 'user_id'>;
|
||||
|
||||
await db.saveFavorite(user, source, id, favoriteWithoutUserId);
|
||||
await db.saveFavorite(authInfo.username, source, id, favoriteWithoutUserId);
|
||||
|
||||
return NextResponse.json({ success: true }, { status: 200 });
|
||||
} catch (err) {
|
||||
@@ -114,13 +114,15 @@ export async function POST(request: NextRequest) {
|
||||
*/
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const username = authInfo.username;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const key = searchParams.get('key');
|
||||
const user = searchParams.get('user');
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (key) {
|
||||
// 删除单条
|
||||
@@ -131,14 +133,14 @@ export async function DELETE(request: NextRequest) {
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
await db.deleteFavorite(user, source, id);
|
||||
await db.deleteFavorite(username, source, id);
|
||||
} else {
|
||||
// 清空全部
|
||||
const all = await db.getAllFavorites(user);
|
||||
const all = await db.getAllFavorites(username);
|
||||
await Promise.all(
|
||||
Object.keys(all).map(async (k) => {
|
||||
const [s, i] = k.split('+');
|
||||
if (s && i) await db.deleteFavorite(user, s, i);
|
||||
if (s && i) await db.deleteFavorite(username, s, i);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { db } from '@/lib/db';
|
||||
import { PlayRecord } from '@/lib/types';
|
||||
|
||||
@@ -9,13 +10,13 @@ export const runtime = 'edge';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const user = searchParams.get('user');
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const records = await db.getAllPlayRecords(user);
|
||||
const records = await db.getAllPlayRecords(authInfo.username);
|
||||
return NextResponse.json(records, { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('获取播放记录失败', err);
|
||||
@@ -28,17 +29,15 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const {
|
||||
user,
|
||||
key,
|
||||
record,
|
||||
}: { user?: string; key: string; record: PlayRecord } = body;
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { key, record }: { key: string; record: PlayRecord } = body;
|
||||
|
||||
if (!key || !record) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Missing key or record' },
|
||||
@@ -75,7 +74,7 @@ export async function POST(request: NextRequest) {
|
||||
save_time: record.save_time,
|
||||
};
|
||||
|
||||
await db.savePlayRecord(user, source, id, recordWithoutUserId);
|
||||
await db.savePlayRecord(authInfo.username, source, id, recordWithoutUserId);
|
||||
|
||||
return NextResponse.json({ success: true }, { status: 200 });
|
||||
} catch (err) {
|
||||
@@ -89,13 +88,15 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const username = authInfo.username;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const key = searchParams.get('key');
|
||||
const user = searchParams.get('user');
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Missing user' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (key) {
|
||||
// 如果提供了 key,删除单条播放记录
|
||||
@@ -107,15 +108,15 @@ export async function DELETE(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
await db.deletePlayRecord(user, source, id);
|
||||
await db.deletePlayRecord(username, source, id);
|
||||
} else {
|
||||
// 未提供 key,则清空全部播放记录
|
||||
// 目前 DbManager 没有对应方法,这里直接遍历删除
|
||||
const all = await db.getAllPlayRecords(user);
|
||||
const all = await db.getAllPlayRecords(username);
|
||||
await Promise.all(
|
||||
Object.keys(all).map(async (k) => {
|
||||
const [s, i] = k.split('+');
|
||||
if (s && i) await db.deletePlayRecord(user, s, i);
|
||||
if (s && i) await db.deletePlayRecord(username, s, i);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
export const runtime = 'edge';
|
||||
@@ -10,22 +11,18 @@ export const runtime = 'edge';
|
||||
const HISTORY_LIMIT = 20;
|
||||
|
||||
/**
|
||||
* GET /api/searchhistory?user=<username>
|
||||
* GET /api/searchhistory
|
||||
* 返回 string[]
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const user = searchParams.get('user')?.trim();
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const history = await db.getSearchHistory(user);
|
||||
const history = await db.getSearchHistory(authInfo.username);
|
||||
return NextResponse.json(history, { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('获取搜索历史失败', err);
|
||||
@@ -38,13 +35,18 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
/**
|
||||
* POST /api/searchhistory
|
||||
* body: { keyword: string, user: string }
|
||||
* body: { keyword: string }
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const keyword: string = body.keyword?.trim();
|
||||
const user: string = body.user?.trim();
|
||||
|
||||
if (!keyword) {
|
||||
return NextResponse.json(
|
||||
@@ -53,17 +55,10 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
await db.addSearchHistory(user, keyword);
|
||||
await db.addSearchHistory(authInfo.username, keyword);
|
||||
|
||||
// 再次获取最新列表,确保客户端与服务端同步
|
||||
const history = await db.getSearchHistory(user);
|
||||
const history = await db.getSearchHistory(authInfo.username);
|
||||
return NextResponse.json(history.slice(0, HISTORY_LIMIT), { status: 200 });
|
||||
} catch (err) {
|
||||
console.error('添加搜索历史失败', err);
|
||||
@@ -75,25 +70,23 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/searchhistory?user=<username>&keyword=<kw>
|
||||
* DELETE /api/searchhistory?keyword=<kw>
|
||||
*
|
||||
* 1. 不带 keyword -> 清空全部搜索历史
|
||||
* 2. 带 keyword=<kw> -> 删除单条关键字
|
||||
*/
|
||||
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();
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'User parameter is required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
// 从 cookie 获取用户信息
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
await db.deleteSearchHistory(user, kw || undefined);
|
||||
const { searchParams } = new URL(request.url);
|
||||
const kw = searchParams.get('keyword')?.trim();
|
||||
|
||||
await db.deleteSearchHistory(authInfo.username, kw || undefined);
|
||||
|
||||
return NextResponse.json({ success: true }, { status: 200 });
|
||||
} catch (err) {
|
||||
|
||||
@@ -46,14 +46,6 @@ function LoginPageClient() {
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// API 已经设置了认证cookie,这里只保存到localStorage用于向后兼容
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('password', password);
|
||||
if (shouldAskUsername) {
|
||||
localStorage.setItem('username', username);
|
||||
}
|
||||
}
|
||||
|
||||
const redirect = searchParams.get('redirect') || '/';
|
||||
router.replace(redirect);
|
||||
} else if (res.status === 401) {
|
||||
@@ -83,12 +75,6 @@ function LoginPageClient() {
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// API 已经设置了认证cookie,这里只保存到localStorage用于向后兼容
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('password', password);
|
||||
localStorage.setItem('username', username);
|
||||
}
|
||||
|
||||
const redirect = searchParams.get('redirect') || '/';
|
||||
router.replace(redirect);
|
||||
} else {
|
||||
|
||||
@@ -23,12 +23,6 @@ export const LogoutButton: React.FC = () => {
|
||||
console.error('注销请求失败:', error);
|
||||
}
|
||||
|
||||
// 清除localStorage中的认证信息(向后兼容)
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem('password');
|
||||
localStorage.removeItem('username');
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
|
||||
57
src/lib/auth.ts
Normal file
57
src/lib/auth.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
// 从cookie获取认证信息 (服务端使用)
|
||||
export function getAuthInfoFromCookie(request: NextRequest): {
|
||||
password?: string;
|
||||
username?: string;
|
||||
signature?: string;
|
||||
timestamp?: number;
|
||||
} | null {
|
||||
const authCookie = request.cookies.get('auth');
|
||||
|
||||
if (!authCookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = decodeURIComponent(authCookie.value);
|
||||
const authData = JSON.parse(decoded);
|
||||
return authData;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 从cookie获取认证信息 (客户端使用)
|
||||
export function getAuthInfoFromBrowserCookie(): {
|
||||
password?: string;
|
||||
username?: string;
|
||||
signature?: string;
|
||||
timestamp?: number;
|
||||
} | null {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析 document.cookie
|
||||
const cookies = document.cookie.split(';').reduce((acc, cookie) => {
|
||||
const [key, value] = cookie.trim().split('=');
|
||||
if (key && value) {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
const authCookie = cookies['auth'];
|
||||
if (!authCookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const decoded = decodeURIComponent(authCookie);
|
||||
const authData = JSON.parse(decoded);
|
||||
return authData;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -24,27 +24,11 @@ export interface PlayRecord {
|
||||
play_time: number; // 播放进度(秒)
|
||||
total_time: number; // 总进度(秒)
|
||||
save_time: number; // 记录保存时间(时间戳)
|
||||
user_id: number; // 用户 ID,本地存储情况下恒为 0
|
||||
}
|
||||
|
||||
// ---- 常量 ----
|
||||
const PLAY_RECORDS_KEY = 'moontv_play_records';
|
||||
|
||||
// +++ 新增:获取当前用户名工具函数 +++
|
||||
/**
|
||||
* 从 localStorage 中读取当前用户名
|
||||
* 如果不存在则返回 undefined
|
||||
*/
|
||||
function getUsername(): string | undefined {
|
||||
if (typeof window === 'undefined') return undefined;
|
||||
try {
|
||||
const name = localStorage.getItem('username')?.trim();
|
||||
return name || undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 环境变量 ----
|
||||
const STORAGE_TYPE = (() => {
|
||||
const raw =
|
||||
@@ -84,10 +68,7 @@ export function generateStorageKey(source: string, id: string): string {
|
||||
export async function getAllPlayRecords(): Promise<Record<string, PlayRecord>> {
|
||||
// 若配置标明使用数据库,则从后端 API 拉取
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
const user = getUsername();
|
||||
return fetchFromApi<Record<string, PlayRecord>>(
|
||||
`/api/playrecords?user=${encodeURIComponent(user ?? '')}`
|
||||
);
|
||||
return fetchFromApi<Record<string, PlayRecord>>(`/api/playrecords`);
|
||||
}
|
||||
|
||||
// 默认 / localstorage 流程
|
||||
@@ -112,21 +93,19 @@ export async function getAllPlayRecords(): Promise<Record<string, PlayRecord>> {
|
||||
export async function savePlayRecord(
|
||||
source: string,
|
||||
id: string,
|
||||
record: Omit<PlayRecord, 'user_id'>
|
||||
record: PlayRecord
|
||||
): Promise<void> {
|
||||
const key = generateStorageKey(source, id);
|
||||
const fullRecord: PlayRecord = { ...record, user_id: 0 };
|
||||
|
||||
// 若配置标明使用数据库,则通过 API 保存
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
const res = await fetch('/api/playrecords', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user, key, record: fullRecord }),
|
||||
body: JSON.stringify({ key, record }),
|
||||
});
|
||||
if (!res.ok) throw new Error(`保存播放记录失败: ${res.status}`);
|
||||
} catch (err) {
|
||||
@@ -144,7 +123,7 @@ export async function savePlayRecord(
|
||||
|
||||
try {
|
||||
const allRecords = await getAllPlayRecords();
|
||||
allRecords[key] = fullRecord;
|
||||
allRecords[key] = record;
|
||||
localStorage.setItem(PLAY_RECORDS_KEY, JSON.stringify(allRecords));
|
||||
} catch (err) {
|
||||
console.error('保存播放记录失败:', err);
|
||||
@@ -165,9 +144,7 @@ export async function deletePlayRecord(
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/playrecords?key=${encodeURIComponent(
|
||||
key
|
||||
)}&user=${encodeURIComponent(getUsername() ?? '')}`,
|
||||
`/api/playrecords?key=${encodeURIComponent(key)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
@@ -206,10 +183,7 @@ export async function getSearchHistory(): Promise<string[]> {
|
||||
// 如果配置为使用数据库,则从后端 API 获取
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
return fetchFromApi<string[]>(
|
||||
`/api/searchhistory?user=${encodeURIComponent(user ?? '')}`
|
||||
);
|
||||
return fetchFromApi<string[]>(`/api/searchhistory`);
|
||||
} catch (err) {
|
||||
console.error('获取搜索历史失败:', err);
|
||||
return [];
|
||||
@@ -243,13 +217,12 @@ 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, user: user ?? '' }),
|
||||
body: JSON.stringify({ keyword: trimmed }),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('保存搜索历史失败:', err);
|
||||
@@ -280,8 +253,7 @@ export async function clearSearchHistory(): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
await fetch(`/api/searchhistory?user=${encodeURIComponent(user ?? '')}`, {
|
||||
await fetch(`/api/searchhistory`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -305,15 +277,9 @@ export async function deleteSearchHistory(keyword: string): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
await fetch(
|
||||
`/api/searchhistory?user=${encodeURIComponent(
|
||||
user ?? ''
|
||||
)}&keyword=${encodeURIComponent(trimmed)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
await fetch(`/api/searchhistory?keyword=${encodeURIComponent(trimmed)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('删除搜索历史失败:', err);
|
||||
}
|
||||
@@ -342,7 +308,6 @@ export interface Favorite {
|
||||
cover: string;
|
||||
total_episodes: number;
|
||||
save_time: number;
|
||||
user_id: number; // 本地存储情况下恒为 0
|
||||
}
|
||||
|
||||
// 收藏在 localStorage 中使用的 key
|
||||
@@ -354,10 +319,7 @@ const FAVORITES_KEY = 'moontv_favorites';
|
||||
export async function getAllFavorites(): Promise<Record<string, Favorite>> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
const user = getUsername();
|
||||
return fetchFromApi<Record<string, Favorite>>(
|
||||
`/api/favorites?user=${encodeURIComponent(user ?? '')}`
|
||||
);
|
||||
return fetchFromApi<Record<string, Favorite>>(`/api/favorites`);
|
||||
}
|
||||
|
||||
// localStorage 模式
|
||||
@@ -381,21 +343,19 @@ export async function getAllFavorites(): Promise<Record<string, Favorite>> {
|
||||
export async function saveFavorite(
|
||||
source: string,
|
||||
id: string,
|
||||
favorite: Omit<Favorite, 'user_id'>
|
||||
favorite: Favorite
|
||||
): Promise<void> {
|
||||
const key = generateStorageKey(source, id);
|
||||
const fullFavorite: Favorite = { ...favorite, user_id: 0 };
|
||||
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
const res = await fetch('/api/favorites', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user, key, favorite: fullFavorite }),
|
||||
body: JSON.stringify({ key, favorite }),
|
||||
});
|
||||
if (!res.ok) throw new Error(`保存收藏失败: ${res.status}`);
|
||||
} catch (err) {
|
||||
@@ -413,7 +373,7 @@ export async function saveFavorite(
|
||||
|
||||
try {
|
||||
const allFavorites = await getAllFavorites();
|
||||
allFavorites[key] = fullFavorite;
|
||||
allFavorites[key] = favorite;
|
||||
localStorage.setItem(FAVORITES_KEY, JSON.stringify(allFavorites));
|
||||
} catch (err) {
|
||||
console.error('保存收藏失败:', err);
|
||||
@@ -433,15 +393,9 @@ export async function deleteFavorite(
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
const res = await fetch(
|
||||
`/api/favorites?key=${encodeURIComponent(
|
||||
key
|
||||
)}&user=${encodeURIComponent(user ?? '')}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
const res = await fetch(`/api/favorites?key=${encodeURIComponent(key)}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!res.ok) throw new Error(`删除收藏失败: ${res.status}`);
|
||||
} catch (err) {
|
||||
console.error('删除收藏到数据库失败:', err);
|
||||
@@ -478,12 +432,7 @@ export async function isFavorited(
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
try {
|
||||
const user = getUsername();
|
||||
const res = await fetch(
|
||||
`/api/favorites?key=${encodeURIComponent(
|
||||
key
|
||||
)}&user=${encodeURIComponent(user ?? '')}`
|
||||
);
|
||||
const res = await fetch(`/api/favorites?key=${encodeURIComponent(key)}`);
|
||||
if (!res.ok) return false;
|
||||
const data = await res.json();
|
||||
return !!data;
|
||||
@@ -505,7 +454,7 @@ export async function isFavorited(
|
||||
export async function toggleFavorite(
|
||||
source: string,
|
||||
id: string,
|
||||
favoriteData?: Omit<Favorite, 'user_id'>
|
||||
favoriteData?: Favorite
|
||||
): Promise<boolean> {
|
||||
const already = await isFavorited(source, id);
|
||||
|
||||
@@ -528,9 +477,8 @@ export async function toggleFavorite(
|
||||
export async function clearAllPlayRecords(): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
const user = getUsername();
|
||||
try {
|
||||
await fetch(`/api/playrecords?user=${encodeURIComponent(user ?? '')}`, {
|
||||
await fetch(`/api/playrecords`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
@@ -551,9 +499,8 @@ export async function clearAllPlayRecords(): Promise<void> {
|
||||
export async function clearAllFavorites(): Promise<void> {
|
||||
// 数据库模式
|
||||
if (STORAGE_TYPE !== 'localstorage') {
|
||||
const user = getUsername();
|
||||
try {
|
||||
await fetch(`/api/favorites?user=${encodeURIComponent(user ?? '')}`, {
|
||||
await fetch(`/api/favorites`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
@@ -63,8 +63,7 @@ export class DbManager {
|
||||
record: Omit<PlayRecord, 'user_id'>
|
||||
): Promise<void> {
|
||||
const key = generateStorageKey(source, id);
|
||||
const fullRecord: PlayRecord = { ...record, user_id: 0 };
|
||||
await this.storage.setPlayRecord(userName, key, fullRecord);
|
||||
await this.storage.setPlayRecord(userName, key, record);
|
||||
}
|
||||
|
||||
async getAllPlayRecords(userName: string): Promise<{
|
||||
@@ -96,11 +95,10 @@ export class DbManager {
|
||||
userName: string,
|
||||
source: string,
|
||||
id: string,
|
||||
favorite: Omit<Favorite, 'user_id'>
|
||||
favorite: Favorite
|
||||
): Promise<void> {
|
||||
const key = generateStorageKey(source, id);
|
||||
const fullFavorite: Favorite = { ...favorite, user_id: 0 };
|
||||
await this.storage.setFavorite(userName, key, fullFavorite);
|
||||
await this.storage.setFavorite(userName, key, favorite);
|
||||
}
|
||||
|
||||
async getAllFavorites(
|
||||
|
||||
@@ -10,7 +10,6 @@ export interface PlayRecord {
|
||||
play_time: number; // 播放进度(秒)
|
||||
total_time: number; // 总进度(秒)
|
||||
save_time: number; // 记录保存时间(时间戳)
|
||||
user_id: number; // 用户ID,localStorage情况下全部为0
|
||||
}
|
||||
|
||||
// 收藏数据结构
|
||||
@@ -19,7 +18,6 @@ export interface Favorite {
|
||||
total_episodes: number; // 总集数
|
||||
title: string;
|
||||
cover: string;
|
||||
user_id: number; // 用户ID,localStorage情况下全部为0
|
||||
save_time: number; // 记录保存时间(时间戳)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
@@ -94,28 +96,6 @@ async function verifySignature(
|
||||
}
|
||||
}
|
||||
|
||||
// 从cookie获取认证信息
|
||||
function getAuthInfoFromCookie(request: NextRequest): {
|
||||
password?: string;
|
||||
username?: string;
|
||||
signature?: string;
|
||||
timestamp?: number;
|
||||
} | null {
|
||||
const authCookie = request.cookies.get('auth');
|
||||
|
||||
if (!authCookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = decodeURIComponent(authCookie.value);
|
||||
const authData = JSON.parse(decoded);
|
||||
return authData;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 重定向到登录页面
|
||||
function redirectToLogin(request: NextRequest, pathname: string): NextResponse {
|
||||
const loginUrl = new URL('/login', request.url);
|
||||
|
||||
Reference in New Issue
Block a user