first commit

This commit is contained in:
JohnsonRan
2025-08-12 21:50:58 +08:00
commit 8b9be4bb19
121 changed files with 36497 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
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';
export const runtime = 'edge';
// 支持的操作类型
type Action = 'add' | 'disable' | 'enable' | 'delete' | 'sort';
interface BaseBody {
action?: Action;
}
export async function POST(request: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
return NextResponse.json(
{
error: '不支持本地存储进行管理员配置',
},
{ status: 400 }
);
}
if (storageType === 'upstash') {
return NextResponse.json(
{
error: 'Upstash 实例请通过配置文件调整',
},
{ status: 400 }
);
}
try {
const body = (await request.json()) as BaseBody & Record<string, any>;
const { 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 || !action || !ACTIONS.includes(action)) {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
// 获取配置与存储
const adminConfig = await getConfig();
const storage: IStorage | null = getStorage();
// 权限与身份校验
if (username !== process.env.USERNAME) {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
switch (action) {
case 'add': {
const { name, type, query } = body as {
name?: string;
type?: 'movie' | 'tv';
query?: string;
};
if (!name || !type || !query) {
return NextResponse.json({ error: '缺少必要参数' }, { status: 400 });
}
// 检查是否已存在相同的查询和类型组合
if (
adminConfig.CustomCategories.some(
(c) => c.query === query && c.type === type
)
) {
return NextResponse.json({ error: '该分类已存在' }, { status: 400 });
}
adminConfig.CustomCategories.push({
name,
type,
query,
from: 'custom',
disabled: false,
});
break;
}
case 'disable': {
const { query, type } = body as {
query?: string;
type?: 'movie' | 'tv';
};
if (!query || !type)
return NextResponse.json(
{ error: '缺少 query 或 type 参数' },
{ status: 400 }
);
const entry = adminConfig.CustomCategories.find(
(c) => c.query === query && c.type === type
);
if (!entry)
return NextResponse.json({ error: '分类不存在' }, { status: 404 });
entry.disabled = true;
break;
}
case 'enable': {
const { query, type } = body as {
query?: string;
type?: 'movie' | 'tv';
};
if (!query || !type)
return NextResponse.json(
{ error: '缺少 query 或 type 参数' },
{ status: 400 }
);
const entry = adminConfig.CustomCategories.find(
(c) => c.query === query && c.type === type
);
if (!entry)
return NextResponse.json({ error: '分类不存在' }, { status: 404 });
entry.disabled = false;
break;
}
case 'delete': {
const { query, type } = body as {
query?: string;
type?: 'movie' | 'tv';
};
if (!query || !type)
return NextResponse.json(
{ error: '缺少 query 或 type 参数' },
{ status: 400 }
);
const idx = adminConfig.CustomCategories.findIndex(
(c) => c.query === query && c.type === type
);
if (idx === -1)
return NextResponse.json({ error: '分类不存在' }, { status: 404 });
const entry = adminConfig.CustomCategories[idx];
if (entry.from === 'config') {
return NextResponse.json(
{ error: '该分类不可删除' },
{ status: 400 }
);
}
adminConfig.CustomCategories.splice(idx, 1);
break;
}
case 'sort': {
const { order } = body as { order?: string[] };
if (!Array.isArray(order)) {
return NextResponse.json(
{ error: '排序列表格式错误' },
{ status: 400 }
);
}
const map = new Map(
adminConfig.CustomCategories.map((c) => [`${c.query}:${c.type}`, c])
);
const newList: typeof adminConfig.CustomCategories = [];
order.forEach((key) => {
const item = map.get(key);
if (item) {
newList.push(item);
map.delete(key);
}
});
// 未在 order 中的保持原顺序
adminConfig.CustomCategories.forEach((item) => {
if (map.has(`${item.query}:${item.type}`)) newList.push(item);
});
adminConfig.CustomCategories = newList;
break;
}
default:
return NextResponse.json({ error: '未知操作' }, { status: 400 });
}
// 持久化到存储
if (storage && typeof (storage as any).setAdminConfig === 'function') {
await (storage as any).setAdminConfig(adminConfig);
}
return NextResponse.json(
{ ok: true },
{
headers: {
'Cache-Control': 'no-store',
},
}
);
} catch (error) {
console.error('分类管理操作失败:', error);
return NextResponse.json(
{
error: '分类管理操作失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,63 @@
/* eslint-disable no-console */
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: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
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;
try {
const config = await getConfig();
const result: AdminConfigResult = {
Role: 'owner',
Config: config,
};
if (username === process.env.USERNAME) {
result.Role = 'owner';
} else {
const user = config.UserConfig.Users.find((u) => u.username === username);
if (user && user.role === 'admin' && !user.banned) {
result.Role = 'admin';
} else {
return NextResponse.json(
{ error: '你是管理员吗你就访问?' },
{ status: 401 }
);
}
}
return NextResponse.json(result, {
headers: {
'Cache-Control': 'no-store', // 管理员配置不缓存
},
});
} catch (error) {
console.error('获取管理员配置失败:', error);
return NextResponse.json(
{
error: '获取管理员配置失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,51 @@
/* eslint-disable no-console */
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: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
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) {
return NextResponse.json({ error: '仅支持站长重置配置' }, { status: 401 });
}
try {
await resetConfig();
return NextResponse.json(
{ ok: true },
{
headers: {
'Cache-Control': 'no-store', // 管理员配置不缓存
},
}
);
} catch (error) {
return NextResponse.json(
{
error: '重置管理员配置失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,118 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
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: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
return NextResponse.json(
{
error: '不支持本地存储进行管理员配置',
},
{ status: 400 }
);
}
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 {
SiteName,
Announcement,
SearchDownstreamMaxPage,
SiteInterfaceCacheTime,
DoubanProxyType,
DoubanProxy,
DoubanImageProxyType,
DoubanImageProxy,
DisableYellowFilter,
} = body as {
SiteName: string;
Announcement: string;
SearchDownstreamMaxPage: number;
SiteInterfaceCacheTime: number;
DoubanProxyType: string;
DoubanProxy: string;
DoubanImageProxyType: string;
DoubanImageProxy: string;
DisableYellowFilter: boolean;
};
// 参数校验
if (
typeof SiteName !== 'string' ||
typeof Announcement !== 'string' ||
typeof SearchDownstreamMaxPage !== 'number' ||
typeof SiteInterfaceCacheTime !== 'number' ||
typeof DoubanProxyType !== 'string' ||
typeof DoubanProxy !== 'string' ||
typeof DoubanImageProxyType !== 'string' ||
typeof DoubanImageProxy !== 'string' ||
typeof DisableYellowFilter !== 'boolean'
) {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
const adminConfig = await getConfig();
const storage = getStorage();
// 权限校验
if (username !== process.env.USERNAME) {
// 管理员
const user = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!user || user.role !== 'admin' || user.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
// 更新缓存中的站点设置
adminConfig.SiteConfig = {
SiteName,
Announcement,
SearchDownstreamMaxPage,
SiteInterfaceCacheTime,
DoubanProxyType,
DoubanProxy,
DoubanImageProxyType,
DoubanImageProxy,
DisableYellowFilter,
};
// 写入数据库
if (storage && typeof (storage as any).setAdminConfig === 'function') {
await (storage as any).setAdminConfig(adminConfig);
}
return NextResponse.json(
{ ok: true },
{
headers: {
'Cache-Control': 'no-store', // 不缓存结果
},
}
);
} catch (error) {
console.error('更新站点配置失败:', error);
return NextResponse.json(
{
error: '更新站点配置失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,169 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
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';
export const runtime = 'edge';
// 支持的操作类型
type Action = 'add' | 'disable' | 'enable' | 'delete' | 'sort';
interface BaseBody {
action?: Action;
}
export async function POST(request: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
return NextResponse.json(
{
error: '不支持本地存储进行管理员配置',
},
{ status: 400 }
);
}
try {
const body = (await request.json()) as BaseBody & Record<string, any>;
const { 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 || !action || !ACTIONS.includes(action)) {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
// 获取配置与存储
const adminConfig = await getConfig();
const storage: IStorage | null = getStorage();
// 权限与身份校验
if (username !== process.env.USERNAME) {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
}
switch (action) {
case 'add': {
const { key, name, api, detail } = body as {
key?: string;
name?: string;
api?: string;
detail?: string;
};
if (!key || !name || !api) {
return NextResponse.json({ error: '缺少必要参数' }, { status: 400 });
}
if (adminConfig.SourceConfig.some((s) => s.key === key)) {
return NextResponse.json({ error: '该源已存在' }, { status: 400 });
}
adminConfig.SourceConfig.push({
key,
name,
api,
detail,
from: 'custom',
disabled: false,
});
break;
}
case 'disable': {
const { key } = body as { key?: string };
if (!key)
return NextResponse.json({ error: '缺少 key 参数' }, { status: 400 });
const entry = adminConfig.SourceConfig.find((s) => s.key === key);
if (!entry)
return NextResponse.json({ error: '源不存在' }, { status: 404 });
entry.disabled = true;
break;
}
case 'enable': {
const { key } = body as { key?: string };
if (!key)
return NextResponse.json({ error: '缺少 key 参数' }, { status: 400 });
const entry = adminConfig.SourceConfig.find((s) => s.key === key);
if (!entry)
return NextResponse.json({ error: '源不存在' }, { status: 404 });
entry.disabled = false;
break;
}
case 'delete': {
const { key } = body as { key?: string };
if (!key)
return NextResponse.json({ error: '缺少 key 参数' }, { status: 400 });
const idx = adminConfig.SourceConfig.findIndex((s) => s.key === key);
if (idx === -1)
return NextResponse.json({ error: '源不存在' }, { status: 404 });
const entry = adminConfig.SourceConfig[idx];
if (entry.from === 'config') {
return NextResponse.json({ error: '该源不可删除' }, { status: 400 });
}
adminConfig.SourceConfig.splice(idx, 1);
break;
}
case 'sort': {
const { order } = body as { order?: string[] };
if (!Array.isArray(order)) {
return NextResponse.json(
{ error: '排序列表格式错误' },
{ status: 400 }
);
}
const map = new Map(adminConfig.SourceConfig.map((s) => [s.key, s]));
const newList: typeof adminConfig.SourceConfig = [];
order.forEach((k) => {
const item = map.get(k);
if (item) {
newList.push(item);
map.delete(k);
}
});
// 未在 order 中的保持原顺序
adminConfig.SourceConfig.forEach((item) => {
if (map.has(item.key)) newList.push(item);
});
adminConfig.SourceConfig = newList;
break;
}
default:
return NextResponse.json({ error: '未知操作' }, { status: 400 });
}
// 持久化到存储
if (storage && typeof (storage as any).setAdminConfig === 'function') {
await (storage as any).setAdminConfig(adminConfig);
}
return NextResponse.json(
{ ok: true },
{
headers: {
'Cache-Control': 'no-store',
},
}
);
} catch (error) {
console.error('视频源管理操作失败:', error);
return NextResponse.json(
{
error: '视频源管理操作失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,337 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console,@typescript-eslint/no-non-null-assertion */
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';
export const runtime = 'edge';
// 支持的操作类型
const ACTIONS = [
'add',
'ban',
'unban',
'setAdmin',
'cancelAdmin',
'setAllowRegister',
'changePassword',
'deleteUser',
] as const;
export async function POST(request: NextRequest) {
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
if (storageType === 'localstorage') {
return NextResponse.json(
{
error: '不支持本地存储进行管理员配置',
},
{ status: 400 }
);
}
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 {
targetUsername, // 目标用户名
targetPassword, // 目标用户密码(仅在添加用户时需要)
allowRegister,
action,
} = body as {
targetUsername?: string;
targetPassword?: string;
allowRegister?: boolean;
action?: (typeof ACTIONS)[number];
};
if (!action || !ACTIONS.includes(action)) {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
if (action !== 'setAllowRegister' && !targetUsername) {
return NextResponse.json({ error: '缺少目标用户名' }, { status: 400 });
}
if (
action !== 'setAllowRegister' &&
action !== 'changePassword' &&
action !== 'deleteUser' &&
username === targetUsername
) {
return NextResponse.json(
{ error: '无法对自己进行此操作' },
{ status: 400 }
);
}
// 获取配置与存储
const adminConfig = await getConfig();
const storage: IStorage | null = getStorage();
// 判定操作者角色
let operatorRole: 'owner' | 'admin';
if (username === process.env.USERNAME) {
operatorRole = 'owner';
} else {
const userEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === username
);
if (!userEntry || userEntry.role !== 'admin' || userEntry.banned) {
return NextResponse.json({ error: '权限不足' }, { status: 401 });
}
operatorRole = 'admin';
}
// 查找目标用户条目
let targetEntry = adminConfig.UserConfig.Users.find(
(u) => u.username === targetUsername
);
if (
targetEntry &&
targetEntry.role === 'owner' &&
action !== 'changePassword'
) {
return NextResponse.json({ error: '无法操作站长' }, { status: 400 });
}
// 权限校验逻辑
const isTargetAdmin = targetEntry?.role === 'admin';
if (action === 'setAllowRegister') {
if (typeof allowRegister !== 'boolean') {
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
}
adminConfig.UserConfig.AllowRegister = allowRegister;
// 保存后直接返回成功(走后面的统一保存逻辑)
} else {
switch (action) {
case 'add': {
if (targetEntry) {
return NextResponse.json({ error: '用户已存在' }, { status: 400 });
}
if (!targetPassword) {
return NextResponse.json(
{ error: '缺少目标用户密码' },
{ status: 400 }
);
}
if (!storage || typeof storage.registerUser !== 'function') {
return NextResponse.json(
{ error: '存储未配置用户注册' },
{ status: 500 }
);
}
await storage.registerUser(targetUsername!, targetPassword);
// 更新配置
adminConfig.UserConfig.Users.push({
username: targetUsername!,
role: 'user',
});
targetEntry =
adminConfig.UserConfig.Users[
adminConfig.UserConfig.Users.length - 1
];
break;
}
case 'ban': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
if (isTargetAdmin) {
// 目标是管理员
if (operatorRole !== 'owner') {
return NextResponse.json(
{ error: '仅站长可封禁管理员' },
{ status: 401 }
);
}
}
targetEntry.banned = true;
break;
}
case 'unban': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
if (isTargetAdmin) {
if (operatorRole !== 'owner') {
return NextResponse.json(
{ error: '仅站长可操作管理员' },
{ status: 401 }
);
}
}
targetEntry.banned = false;
break;
}
case 'setAdmin': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
if (targetEntry.role === 'admin') {
return NextResponse.json(
{ error: '该用户已是管理员' },
{ status: 400 }
);
}
if (operatorRole !== 'owner') {
return NextResponse.json(
{ error: '仅站长可设置管理员' },
{ status: 401 }
);
}
targetEntry.role = 'admin';
break;
}
case 'cancelAdmin': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
if (targetEntry.role !== 'admin') {
return NextResponse.json(
{ error: '目标用户不是管理员' },
{ status: 400 }
);
}
if (operatorRole !== 'owner') {
return NextResponse.json(
{ error: '仅站长可取消管理员' },
{ status: 401 }
);
}
targetEntry.role = 'user';
break;
}
case 'changePassword': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
if (!targetPassword) {
return NextResponse.json({ error: '缺少新密码' }, { status: 400 });
}
// 权限检查:不允许修改站长密码
if (targetEntry.role === 'owner') {
return NextResponse.json(
{ error: '无法修改站长密码' },
{ status: 401 }
);
}
if (
isTargetAdmin &&
operatorRole !== 'owner' &&
username !== targetUsername
) {
return NextResponse.json(
{ error: '仅站长可修改其他管理员密码' },
{ status: 401 }
);
}
if (!storage || typeof storage.changePassword !== 'function') {
return NextResponse.json(
{ error: '存储未配置密码修改功能' },
{ status: 500 }
);
}
await storage.changePassword(targetUsername!, targetPassword);
break;
}
case 'deleteUser': {
if (!targetEntry) {
return NextResponse.json(
{ error: '目标用户不存在' },
{ status: 404 }
);
}
// 权限检查:站长可删除所有用户(除了自己),管理员可删除普通用户
if (username === targetUsername) {
return NextResponse.json(
{ error: '不能删除自己' },
{ status: 400 }
);
}
if (isTargetAdmin && operatorRole !== 'owner') {
return NextResponse.json(
{ error: '仅站长可删除管理员' },
{ status: 401 }
);
}
if (!storage || typeof storage.deleteUser !== 'function') {
return NextResponse.json(
{ error: '存储未配置用户删除功能' },
{ status: 500 }
);
}
await storage.deleteUser(targetUsername!);
// 从配置中移除用户
const userIndex = adminConfig.UserConfig.Users.findIndex(
(u) => u.username === targetUsername
);
if (userIndex > -1) {
adminConfig.UserConfig.Users.splice(userIndex, 1);
}
break;
}
default:
return NextResponse.json({ error: '未知操作' }, { status: 400 });
}
}
// 将更新后的配置写入数据库
if (storage && typeof (storage as any).setAdminConfig === 'function') {
await (storage as any).setAdminConfig(adminConfig);
}
return NextResponse.json(
{ ok: true },
{
headers: {
'Cache-Control': 'no-store', // 管理员配置不缓存
},
}
);
} catch (error) {
console.error('用户管理操作失败:', error);
return NextResponse.json(
{
error: '用户管理操作失败',
details: (error as Error).message,
},
{ status: 500 }
);
}
}