update pay function
This commit is contained in:
@@ -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 />;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// app/ai-chat/test-auth/page.tsx
|
||||||
|
import TestAuth from '../generate-code/test-auth';
|
||||||
|
|
||||||
|
export default function TestAuthPage() {
|
||||||
|
return <TestAuth />;
|
||||||
|
}
|
||||||
@@ -48,8 +48,13 @@ export default function DemoPage() {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>🔥 最新集成:</strong>
|
<strong>🔥 最新集成:</strong>
|
||||||
<a href="/ai-chat/generate-code" className="text-blue-600 hover:underline">AI 代码助手</a>
|
<a href="/ai-chat/generate-code-simple" className="text-blue-600 hover:underline">AI 代码助手(简化版)</a>
|
||||||
- 漂亮界面 + MCP 功能
|
- 直接集成,无复杂组件
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>🧪 测试页面:</strong>
|
||||||
|
<a href="/ai-chat/test-auth" className="text-blue-600 hover:underline">认证测试</a>
|
||||||
|
- 检查认证和图片路径
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>最全功能:</strong>
|
<strong>最全功能:</strong>
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ const MCPLayout = ({ children }: Props) => {
|
|||||||
const [visibleSidebar, setVisibleSidebar] = useState(false);
|
const [visibleSidebar, setVisibleSidebar] = useState(false);
|
||||||
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
|
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
|
||||||
|
|
||||||
|
console.log('[MCPLayout] Auth state:', {
|
||||||
|
user,
|
||||||
|
isAuthenticated,
|
||||||
|
canAccessChat,
|
||||||
|
authLoading
|
||||||
|
});
|
||||||
|
|
||||||
// 权限检查
|
// 权限检查
|
||||||
if (authLoading) {
|
if (authLoading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ export function useAuth() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 组件挂载时检查认证状态
|
// 组件挂载时检查认证状态
|
||||||
const verifyAuth = async () => {
|
const verifyAuth = async () => {
|
||||||
|
console.log('[useAuth] Starting auth verification...');
|
||||||
try {
|
try {
|
||||||
const info = await checkAuth();
|
const info = await checkAuth();
|
||||||
|
console.log('[useAuth] Auth info received:', info);
|
||||||
setAuthInfo(info);
|
setAuthInfo(info);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Auth verification failed:', error);
|
console.error('[useAuth] Auth verification failed:', error);
|
||||||
setAuthInfo({
|
setAuthInfo({
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
message: '认证检查失败',
|
message: '认证检查失败',
|
||||||
@@ -36,7 +38,10 @@ export function useAuth() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...authInfo,
|
user: authInfo.user,
|
||||||
|
isAuthenticated: authInfo.isAuthenticated || false,
|
||||||
|
canAccessChat: authInfo.canAccessChat || false,
|
||||||
|
message: authInfo.message,
|
||||||
loading,
|
loading,
|
||||||
refresh: async () => {
|
refresh: async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@@ -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;
|
||||||
Reference in New Issue
Block a user