ai问片增加权限开关

This commit is contained in:
mtvpls
2025-12-29 15:12:40 +08:00
parent 0a66626545
commit 1a38519a3e
7 changed files with 60 additions and 12 deletions

View File

@@ -7676,6 +7676,9 @@ const AIConfigComponent = ({
const [enableVideoCardEntry, setEnableVideoCardEntry] = useState(true);
const [enablePlayPageEntry, setEnablePlayPageEntry] = useState(true);
// 权限控制
const [allowRegularUsers, setAllowRegularUsers] = useState(true);
// 高级设置
const [temperature, setTemperature] = useState(0.7);
const [maxTokens, setMaxTokens] = useState(1000);
@@ -7697,6 +7700,7 @@ const AIConfigComponent = ({
setEnableHomepageEntry(config.AIConfig.EnableHomepageEntry !== false);
setEnableVideoCardEntry(config.AIConfig.EnableVideoCardEntry !== false);
setEnablePlayPageEntry(config.AIConfig.EnablePlayPageEntry !== false);
setAllowRegularUsers(config.AIConfig.AllowRegularUsers !== false);
setTemperature(config.AIConfig.Temperature ?? 0.7);
setMaxTokens(config.AIConfig.MaxTokens ?? 1000);
setSystemPrompt(config.AIConfig.SystemPrompt || '');
@@ -7726,6 +7730,7 @@ const AIConfigComponent = ({
EnableHomepageEntry: enableHomepageEntry,
EnableVideoCardEntry: enableVideoCardEntry,
EnablePlayPageEntry: enablePlayPageEntry,
AllowRegularUsers: allowRegularUsers,
Temperature: temperature,
MaxTokens: maxTokens,
SystemPrompt: systemPrompt,
@@ -8014,6 +8019,33 @@ const AIConfigComponent = ({
))}
</div>
{/* 权限控制 */}
<div className='space-y-3 p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
<h4 className='text-sm font-semibold text-gray-900 dark:text-gray-100 mb-3'>
</h4>
<div className='flex items-center justify-between py-2'>
<div>
<div className='text-sm font-medium text-gray-900 dark:text-gray-100'>
使
</div>
<div className='text-xs text-gray-500 dark:text-gray-400'>
使AI问片功能
</div>
</div>
<label className='relative inline-flex items-center cursor-pointer'>
<input
type='checkbox'
checked={allowRegularUsers}
onChange={(e) => setAllowRegularUsers(e.target.checked)}
className='sr-only peer'
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-yellow-300 dark:peer-focus:ring-yellow-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600"></div>
</label>
</div>
</div>
{/* 高级设置 */}
<details className='p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
<summary className='text-sm font-semibold text-gray-900 dark:text-gray-100 cursor-pointer'>

View File

@@ -57,6 +57,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry,
EnableVideoCardEntry,
EnablePlayPageEntry,
AllowRegularUsers,
Temperature,
MaxTokens,
SystemPrompt,
@@ -89,6 +90,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry: boolean;
EnableVideoCardEntry: boolean;
EnablePlayPageEntry: boolean;
AllowRegularUsers: boolean;
Temperature?: number;
MaxTokens?: number;
SystemPrompt?: string;
@@ -124,6 +126,7 @@ export async function POST(request: NextRequest) {
typeof EnableHomepageEntry !== 'boolean' ||
typeof EnableVideoCardEntry !== 'boolean' ||
typeof EnablePlayPageEntry !== 'boolean' ||
typeof AllowRegularUsers !== 'boolean' ||
(Temperature !== undefined && typeof Temperature !== 'number') ||
(MaxTokens !== undefined && typeof MaxTokens !== 'number') ||
(SystemPrompt !== undefined && typeof SystemPrompt !== 'string')
@@ -171,6 +174,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry,
EnableVideoCardEntry,
EnablePlayPageEntry,
AllowRegularUsers,
Temperature,
MaxTokens,
SystemPrompt,

View File

@@ -8,6 +8,7 @@ import {
VideoContext,
} from '@/lib/ai-orchestrator';
import { getConfig } from '@/lib/config';
import { db } from '@/lib/db';
export const runtime = 'nodejs';
@@ -72,9 +73,6 @@ async function streamClaudeChat(
maxTokens: number;
}
): Promise<ReadableStream> {
// Claude API格式: 移除system消息,单独传递
const userMessages = messages.filter((m) => m.role !== 'system');
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
@@ -87,7 +85,7 @@ async function streamClaudeChat(
max_tokens: config.maxTokens,
temperature: config.temperature,
system: systemPrompt,
messages: userMessages,
messages: messages,
stream: true,
}),
});
@@ -187,7 +185,23 @@ export async function POST(request: NextRequest) {
);
}
// 3. 解析请求参数
// 3. 权限检查:如果不允许普通用户使用,检查用户角色
if (!aiConfig.AllowRegularUsers) {
const username = authInfo.username;
// 站长始终有权限
if (username !== process.env.USERNAME) {
// 检查是否为管理员
const userInfo = await db.getUserInfoV2(username);
if (!userInfo || (userInfo.role !== 'admin' && userInfo.role !== 'owner') || userInfo.banned) {
return NextResponse.json(
{ error: '该功能仅限站长和管理员使用' },
{ status: 403 }
);
}
}
}
// 4. 解析请求参数
const body = (await request.json()) as ChatRequest;
const { message, context, history = [] } = body;
@@ -214,11 +228,11 @@ export async function POST(request: NextRequest) {
tavilyApiKey: aiConfig.TavilyApiKey,
serperApiKey: aiConfig.SerperApiKey,
serpApiKey: aiConfig.SerpApiKey,
// 决策模型配置固定使用自定义provider
// 决策模型配置固定使用自定义provider复用主模型的API配置
enableDecisionModel: aiConfig.EnableDecisionModel,
decisionProvider: 'custom',
decisionApiKey: aiConfig.DecisionCustomApiKey,
decisionBaseURL: aiConfig.DecisionCustomBaseURL,
decisionApiKey: aiConfig.CustomApiKey,
decisionBaseURL: aiConfig.CustomBaseURL,
decisionModel: aiConfig.DecisionCustomModel,
}
);

View File

@@ -114,7 +114,6 @@ export async function POST(request: NextRequest) {
metaInfo.folders[key] = {
folderName: folderName,
tmdb_id: tmdbId || null,
douban_id: doubanId || null,
title: title,
poster_path: posterPath,
release_date: releaseDate || '',

View File

@@ -130,7 +130,6 @@ export async function GET(request: NextRequest) {
id: key,
folder: info.folderName,
tmdbId: info.tmdb_id,
doubanId: info.douban_id,
title: info.title,
poster: getTMDBImageUrl(info.poster_path),
releaseDate: info.release_date,

View File

@@ -5641,8 +5641,6 @@ function PlayPageClient() {
title: detail.title,
year: detail.year,
douban_id: videoDoubanId !== 0 ? videoDoubanId : undefined,
tmdb_id: detail.tmdb_id,
type: detail.type === 'movie' ? 'movie' : 'tv',
currentEpisode: currentEpisodeIndex + 1,
}}
welcomeMessage={`想了解《${detail.title}》的更多信息吗?我可以帮你查询剧情、演员、评价等。`}

View File

@@ -147,6 +147,8 @@ export interface AdminConfig {
EnableHomepageEntry: boolean; // 首页入口开关
EnableVideoCardEntry: boolean; // VideoCard入口开关
EnablePlayPageEntry: boolean; // 播放页入口开关
// 权限控制
AllowRegularUsers: boolean; // 是否允许普通用户使用AI问片关闭后仅站长和管理员可用
// 高级设置
Temperature?: number; // AI温度参数0-2默认0.7
MaxTokens?: number; // 最大回复token数默认1000