update pay function

This commit is contained in:
2025-11-22 10:37:15 +08:00
parent 40b57c1a81
commit 15d521dd59
7 changed files with 450 additions and 4 deletions

View File

@@ -0,0 +1,6 @@
// app/ai-chat/generate-code-simple/page.tsx - 简化版 MCP 集成
import SimpleMCPGenerateCodePage from "@/templates/GenerateCodePage/SimpleMCP";
export default function Page() {
return <SimpleMCPGenerateCodePage />;
}

View File

@@ -0,0 +1,81 @@
// 测试页面 - 检查认证是否工作
'use client';
import { useEffect, useState } from 'react';
export default function TestAuth() {
const [authData, setAuthData] = useState<any>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://valuefrontier.cn/api/auth/session', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
.then(res => {
console.log('Response status:', res.status);
console.log('Response headers:', res.headers);
return res.json();
})
.then(data => {
console.log('Auth data received:', data);
setAuthData(data);
setLoading(false);
})
.catch(err => {
console.error('Auth check error:', err);
setError(err.message);
setLoading(false);
});
}, []);
return (
<div style={{ padding: '20px' }}>
<h1></h1>
{loading && <p>...</p>}
{error && (
<div style={{ color: 'red' }}>
<h2></h2>
<p>{error}</p>
</div>
)}
{authData && (
<div>
<h2></h2>
<pre>{JSON.stringify(authData, null, 2)}</pre>
{authData.user?.avatar_url && (
<div>
<h3></h3>
<img
src={authData.user.avatar_url}
alt="用户头像"
style={{ width: '100px', height: '100px', borderRadius: '50%' }}
/>
</div>
)}
</div>
)}
<div style={{ marginTop: '20px' }}>
<h3></h3>
<p>1. </p>
<img src="/images/sent.svg" alt="test1" style={{ width: '50px' }} />
<p>2. basePath </p>
<img src="/ai-chat/images/sent.svg" alt="test2" style={{ width: '50px' }} />
<p>3. URL</p>
<img src="https://valuefrontier.cn/ai-chat/images/sent.svg" alt="test3" style={{ width: '50px' }} />
</div>
</div>
);
}

View File

@@ -0,0 +1,6 @@
// app/ai-chat/test-auth/page.tsx
import TestAuth from '../generate-code/test-auth';
export default function TestAuthPage() {
return <TestAuth />;
}

View File

@@ -48,8 +48,13 @@ export default function DemoPage() {
</li>
<li>
<strong>🔥 </strong>
<a href="/ai-chat/generate-code" className="text-blue-600 hover:underline">AI </a>
- + MCP
<a href="/ai-chat/generate-code-simple" className="text-blue-600 hover:underline">AI </a>
-
</li>
<li>
<strong>🧪 </strong>
<a href="/ai-chat/test-auth" className="text-blue-600 hover:underline"></a>
-
</li>
<li>
<strong></strong>

View File

@@ -22,6 +22,13 @@ const MCPLayout = ({ children }: Props) => {
const [visibleSidebar, setVisibleSidebar] = useState(false);
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
console.log('[MCPLayout] Auth state:', {
user,
isAuthenticated,
canAccessChat,
authLoading
});
// 权限检查
if (authLoading) {
return (

View File

@@ -13,11 +13,13 @@ export function useAuth() {
useEffect(() => {
// 组件挂载时检查认证状态
const verifyAuth = async () => {
console.log('[useAuth] Starting auth verification...');
try {
const info = await checkAuth();
console.log('[useAuth] Auth info received:', info);
setAuthInfo(info);
} catch (error) {
console.error('Auth verification failed:', error);
console.error('[useAuth] Auth verification failed:', error);
setAuthInfo({
isAuthenticated: false,
message: '认证检查失败',
@@ -36,7 +38,10 @@ export function useAuth() {
}, []);
return {
...authInfo,
user: authInfo.user,
isAuthenticated: authInfo.isAuthenticated || false,
canAccessChat: authInfo.canAccessChat || false,
message: authInfo.message,
loading,
refresh: async () => {
setLoading(true);

View File

@@ -0,0 +1,336 @@
"use client";
import { useState, useEffect } from "react";
import Layout from "@/components/Layout";
import Chat from "@/components/Chat";
import Question from "@/components/Question";
import Answer from "@/components/Answer";
import CodeEditor from "@/components/CodeEditor";
import { mcpService } from '@/services/mcp-real';
interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: Date;
code?: {
language: string;
content: string;
title?: string;
};
isStreaming?: boolean;
}
interface User {
id: number;
username?: string;
nickname?: string;
email?: string;
avatar_url?: string;
subscription_type?: string;
subscription_days_left?: number;
is_subscription_active?: boolean;
}
const SimpleMCPGenerateCodePage = () => {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [currentStreamingContent, setCurrentStreamingContent] = useState('');
const [currentStreamingId, setCurrentStreamingId] = useState<string | null>(null);
// 认证状态
const [user, setUser] = useState<User | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [canAccessChat, setCanAccessChat] = useState(false);
const [authLoading, setAuthLoading] = useState(true);
// 检查认证状态
useEffect(() => {
console.log('[SimpleMCP] Checking authentication...');
fetch('https://valuefrontier.cn/api/auth/session', {
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
.then(res => {
console.log('[SimpleMCP] Auth response status:', res.status);
if (!res.ok) throw new Error('Auth check failed');
return res.json();
})
.then(data => {
console.log('[SimpleMCP] Auth data:', data);
if (data.isAuthenticated && data.user) {
setUser(data.user);
setIsAuthenticated(true);
// 检查订阅状态
const subscriptionType = (data.user.subscription_type || '').toLowerCase();
const isActive = data.user.is_subscription_active === true;
const canAccess = ['max', 'premium', 'pro'].includes(subscriptionType) || isActive;
setCanAccessChat(canAccess);
console.log('[SimpleMCP] User can access chat:', canAccess);
}
})
.catch(err => {
console.error('[SimpleMCP] Auth check error:', err);
})
.finally(() => {
setAuthLoading(false);
});
}, []);
const extractCodeFromResponse = (content: string) => {
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
const matches = [...content.matchAll(codeBlockRegex)];
if (matches.length > 0) {
const [, language = 'javascript', code] = matches[0];
return {
hasCode: true,
language,
code: code.trim(),
textBefore: content.substring(0, matches[0].index),
textAfter: content.substring((matches[0].index || 0) + matches[0][0].length)
};
}
return { hasCode: false, content };
};
const handleSend = async () => {
if (!input.trim() || isLoading) return;
const userMessage: Message = {
id: Date.now().toString(),
role: 'user',
content: input,
timestamp: new Date(),
};
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsLoading(true);
setCurrentStreamingContent('');
try {
const assistantMessageId = (Date.now() + 1).toString();
setCurrentStreamingId(assistantMessageId);
const assistantMessage: Message = {
id: assistantMessageId,
role: 'assistant',
content: '',
timestamp: new Date(),
isStreaming: true,
};
setMessages(prev => [...prev, assistantMessage]);
let fullContent = '';
for await (const chunk of mcpService.streamMessage(input)) {
fullContent += chunk;
setCurrentStreamingContent(fullContent);
}
const codeInfo = extractCodeFromResponse(fullContent);
setMessages(prev =>
prev.map(msg =>
msg.id === assistantMessageId
? {
...msg,
content: codeInfo.hasCode
? (codeInfo.textBefore || '这是生成的代码:')
: fullContent,
code: codeInfo.hasCode
? {
language: codeInfo.language!,
content: codeInfo.code!,
title: '生成的代码'
}
: undefined,
isStreaming: false
}
: msg
)
);
setCurrentStreamingContent('');
setCurrentStreamingId(null);
} catch (error) {
console.error('Send message error:', error);
setMessages(prev => [
...prev,
{
id: Date.now().toString(),
role: 'system',
content: '抱歉,发送消息时出错了。请稍后重试。',
timestamp: new Date(),
},
]);
} finally {
setIsLoading(false);
}
};
// 加载中
if (authLoading) {
return (
<Layout>
<div className="flex items-center justify-center h-96">
<div className="text-center">
<div className="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-lg">...</p>
</div>
</div>
</Layout>
);
}
// 未登录
if (!isAuthenticated) {
return (
<Layout>
<div className="flex flex-col items-center justify-center h-96">
<h2 className="text-2xl mb-4"></h2>
<p className="text-gray-600 mb-6">使 AI </p>
<a
href="https://valuefrontier.cn/auth/sign-in?redirect=/ai-chat/generate-code"
className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700"
>
</a>
</div>
</Layout>
);
}
// 未订阅
if (!canAccessChat) {
return (
<Layout>
<div className="flex flex-col items-center justify-center h-96">
<h2 className="text-2xl mb-4">使 AI </h2>
<p className="text-gray-600 mb-2"></p>
<p className="text-sm text-gray-500 mb-6">
{user?.subscription_type || '未订阅'}
</p>
<a
href="https://valuefrontier.cn/subscription"
className="bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700"
>
</a>
</div>
</Layout>
);
}
return (
<Layout>
{/* 用户信息显示 */}
{user && (
<div className="fixed bottom-4 left-4 z-50 bg-white rounded-lg shadow-lg p-3 flex items-center space-x-3">
{user.avatar_url ? (
<img
src={user.avatar_url}
alt={user.username || user.nickname}
className="w-10 h-10 rounded-full"
/>
) : (
<div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center text-white font-semibold">
{(user.username || user.nickname || 'U')[0].toUpperCase()}
</div>
)}
<div>
<p className="font-medium text-sm">{user.username || user.nickname}</p>
<p className="text-xs text-gray-500">
{user.subscription_type === 'max' ? '🌟 MAX会员' : '高级会员'}
{user.subscription_days_left ? ` · ${user.subscription_days_left}` : ''}
</p>
</div>
</div>
)}
<Chat>
{/* 输入框 */}
<div className="fixed bottom-0 left-0 right-0 bg-white border-t p-4">
<div className="max-w-4xl mx-auto flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="输入您的问题..."
className="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isLoading}
/>
<button
onClick={handleSend}
disabled={isLoading || !input.trim()}
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
{isLoading ? '发送中...' : '发送'}
</button>
</div>
</div>
{/* 消息列表 */}
<div className="pb-20">
{messages.length === 0 ? (
<div className="text-center text-gray-500 py-12">
<h3 className="text-xl mb-4">👋 使 AI </h3>
<p></p>
</div>
) : (
messages.map((msg) => (
<div key={msg.id}>
{msg.role === 'user' ? (
<Question>{msg.content}</Question>
) : msg.role === 'assistant' ? (
<Answer>
{msg.isStreaming ? (
<div className="whitespace-pre-wrap">
{msg.id === currentStreamingId ? currentStreamingContent : msg.content}
{msg.id === currentStreamingId && !currentStreamingContent && (
<span className="inline-block animate-pulse">...</span>
)}
</div>
) : (
<>
{msg.content && <div>{msg.content}</div>}
{msg.code && (
<CodeEditor
title={msg.code.title || '生成的代码'}
language={msg.code.language}
initialCode={msg.code.content}
onCodeChange={() => {}}
isGenerating={false}
readOnly={false}
/>
)}
</>
)}
</Answer>
) : (
<div className="px-4 py-2 bg-yellow-50 text-yellow-800 rounded-lg">
{msg.content}
</div>
)}
</div>
))
)}
</div>
</Chat>
</Layout>
);
};
export default SimpleMCPGenerateCodePage;