DeepSeek R1
- OpenAI 发布推理模型 O1 后,刻意隐藏了推理链,而 DeepSeek 通过纯粹的强化学习技术复现了 OpenAI o1 的能力;
- DeepSeek 在非常有限的算力资源支持下,通过强大的算法创新,突破了算力瓶颈,对于 AI 发展具有里程碑意义;
- 目前 DeepSeek 已经火出圈,所以忍不住想试一下 DeepSeek R1 的编程能力。
任务
- 在 CloudFlare workers 上部署一个简单的 DeepSeek R1 聊天程序;
- 聊天模型是 CloudFlare 提供的 Workers AI 中的 @cf/deepseek-ai/deepseek-r1-distill-qwen-32b,这个不是671b大小的完全体 DeepSeek R1,只是一个采用千问模型蒸馏的32b大小的版本;
- 也不是从头开始编写,是在之前已经部署的一个版本上进行修改,修改内容是增加 Latex 格式数学公式渲染的支持;
- 由于是一个比较简单的任务,想看看哪个模型能够一次成功。
模型
- 编程工具还是采用免费版的 Cursor ;
- 由于是免费版,不能使用满血版的 O1 模型;
- 对比的三个模型是:DeepSeek R1 , Claude 3.5 Sonnet ,chatGPT O3 mini;
结果
- Claude 3.5 Sonnet 不但没有完成任务,还把程序改得不能工作了;
- DeepSeek R1 虽然也没能完成任务,但程序还是能够工作的,并且用户输入的公式能够渲染,只是AI输出的内容没能渲染成功;
- chatGPT O3 mini 成功完成任务;
- 不得不说, OpenAI 还是标杆。
聊天程序网址
DeepSeek R1 聊天
源代码
- 下面是用 Cursor 生成的代码,我不懂 JS,完全看不懂细节,只能根据变量名猜测功能🤣,所以优化是谈不上的,能工作就行😎
// 生成唯一ID的辅助函数
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// 添加本地存储类
class LocalStorage {
constructor() {
this.store = new Map();
}
async put(key, value) {
this.store.set(key, value);
}
async get(key) {
return this.store.get(key);
}
async delete(key) {
this.store.delete(key);
}
async list() {
return {
keys: Array.from(this.store.keys()).map(name => ({ name }))
};
}
}
// 修改主入口,添加本地存储
export default {
async fetch(request, env) {
// 如果环境中没有 CHAT_HISTORY,使用本地存储
if (!env.CHAT_HISTORY) {
env.CHAT_HISTORY = new LocalStorage();
}
try {
if (request.method === 'GET') {
return await handleGet(request, env);
}
if (request.method === 'POST') {
return await handlePost(request, env);
}
return new Response('不支持的请求方法', { status: 405 });
} catch (error) {
console.error('请求处理错误:', error);
return Response.json({ error: error.message }, { status: 500 });
}
}
};
// GET请求处理
async function handleGet(request, env) {
const url = new URL(request.url);
const path = url.pathname;
// 未登录用户会被重定向到登录页
if (path !== '/login' && !await checkAuth(request)) {
return Response.redirect(`${url.origin}/login`, 302);
}
// 登录页面路由
if (path === '/login') {
return new Response(loginHtml, {
headers: { 'content-type': 'text/html;charset=UTF-8' },
});
}
switch (path) {
case '/conversations':
return await getConversations(env);
case '/history':
return await getHistory(url.searchParams.get('id'), env);
default:
return new Response(html, {
headers: { 'content-type': 'text/html;charset=UTF-8' },
});
}
}
// POST请求处理
async function handlePost(request, env) {
const { message, conversationId, action, title, password, history } = await request.json();
// 处理登录请求
if (action === 'login') {
if (!env.APP_PASSWORD) {
console.error('未设置 APP_PASSWORD 环境变量');
return Response.json({ error: '系统配置错误' }, { status: 500 });
}
if (password === env.APP_PASSWORD) {
return new Response(null, {
status: 200,
headers: {
'Set-Cookie': 'auth_token=valid_token; Path=/; HttpOnly; SameSite=Strict',
}
});
}
return Response.json({ error: '密码错误' }, { status: 401 });
}
// 验证其他请求的访问权限
if (!await checkAuth(request)) {
return Response.json({ error: '未登录' }, { status: 401 });
}
switch (action) {
case 'updateTitle':
return await updateTitle(conversationId, title, env);
case 'new':
return Response.json({ conversationId: generateId() });
case 'delete':
return await deleteConversation(conversationId, env);
case 'import':
// 处理导入的对话历史
if (!conversationId || !history) {
return Response.json({ error: '缺少必要参数' }, { status: 400 });
}
await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
return Response.json({ success: true });
default:
return await handleChat(message, conversationId, env);
}
}
// 对话相关的处理函数
async function getConversations(env) {
const list = await env.CHAT_HISTORY.list();
const conversations = [];
for (const key of list.keys) {
const data = await env.CHAT_HISTORY.get(key.name);
const history = JSON.parse(data);
conversations.push({
id: key.name,
title: history.title || '未命名对话',
lastMessage: history.messages?.[history.messages.length - 1]?.content || '',
timestamp: history.timestamp || Date.now()
});
}
conversations.sort((a, b) => b.timestamp - a.timestamp);
return Response.json(conversations);
}
async function getHistory(conversationId, env) {
if (!conversationId) return Response.json([]);
const savedHistory = await env.CHAT_HISTORY.get(conversationId);
return Response.json(savedHistory ? JSON.parse(savedHistory) : []);
}
async function updateTitle(conversationId, title, env) {
if (!conversationId || !title) {
return Response.json({ error: '缺少必要参数' }, { status: 400 });
}
const savedHistory = await env.CHAT_HISTORY.get(conversationId);
if (!savedHistory) {
return Response.json({ error: '对话不存在' }, { status: 404 });
}
const history = JSON.parse(savedHistory);
history.title = title;
history.timestamp = Date.now();
await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
return Response.json({ success: true });
}
async function deleteConversation(conversationId, env) {
if (!conversationId) {
return Response.json({ error: '缺少对话ID' }, { status: 400 });
}
await env.CHAT_HISTORY.delete(conversationId);
return Response.json({ success: true });
}
// 处理聊天消息
async function handleChat(message, conversationId, env) {
if (!message || !conversationId) {
return Response.json({ error: '缺少必要参数' }, { status: 400 });
}
const chat = {
messages: [
{ role: 'system', content: '你是一位博学的专家,用中文给出简洁而有用的答案,并采用latex格式输出数学公式。' },
{ role: 'user', content: message }
],
stream: true,
max_tokens: 4096,
temperature: 0.5
};
try {
return await processChat(message, conversationId, chat, env);
} catch (error) {
console.error('处理聊天消息错误:', error);
return Response.json({ error: error.message }, { status: 500 });
}
}
// 添加处理聊天消息的函数
async function processChat(message, conversationId, chat, env) {
// 获取历史记录
const savedHistory = await env.CHAT_HISTORY.get(conversationId) || '{"messages":[], "title":"未命名对话"}';
const history = JSON.parse(savedHistory);
if (!history.messages) history.messages = [];
// 保存用户消息
history.messages.push({ role: 'user', content: message });
history.timestamp = Date.now();
await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
// 获取 AI 响应
const stream = await env.AI.run('@cf/deepseek-ai/deepseek-r1-distill-qwen-32b', chat);
// 处理流式响应
const transformStream = new TransformStream({
async transform(chunk, controller) {
const text = new TextDecoder().decode(chunk);
controller.enqueue(chunk);
const lines = text.split('\n').filter(line => line.trim() !== '');
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6);
if (jsonStr.trim() === '[DONE]') continue;
try {
const data = JSON.parse(jsonStr);
if (data.response) {
// 累积 AI 响应
if (!history.messages[history.messages.length - 1] ||
history.messages[history.messages.length - 1].role !== 'assistant') {
history.messages.push({ role: 'assistant', content: data.response });
} else {
history.messages[history.messages.length - 1].content += data.response;
}
// 定期保存到KV
await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
}
} catch (e) {
console.error('解析响应JSON失败:', e);
}
}
}
},
async flush() {
// 确保最后一次保存
await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
}
});
// 返回转换后的流
return new Response(stream.pipeThrough(transformStream), {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
// HTML模板
const html = `<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek R1 32B</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>
<link href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/toastify-js"><\/script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
height: 100vh;
}
#app-container {
display: flex;
height: 100vh;
width: 100%;
}
#sidebar {
width: 280px;
border-right: 1px solid #ccc;
padding: 20px;
background-color: #f8f9fa;
height: 100%;
overflow-y: auto;
box-sizing: border-box;
}
#main-content {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
}
#chat-container {
flex: 1;
border: 1px solid #ccc;
border-radius: 5px;
overflow-y: auto;
padding: 20px;
margin-bottom: 20px;
background-color: white;
}
.message {
margin: 10px 0;
padding: 10px;
border-radius: 5px;
}
.message pre {
background-color: #f0f0f0;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
.message code {
font-family: monospace;
background-color: #f0f0f0;
padding: 2px 4px;
border-radius: 3px;
}
.user-message {
background-color: #e3f2fd;
margin-left: 20%;
}
.ai-message {
background-color: #f5f5f5;
margin-right: 20%;
}
#input-container {
display: flex;
gap: 10px;
padding: 10px 0;
}
#message-input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #0070f3;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0051a2;
}
.loading {
position: relative;
opacity: 0.7;
pointer-events: none;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid #ccc;
border-top-color: #333;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.search-container {
padding: 10px 0;
margin-bottom: 10px;
}
#search-input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.conversation-item {
padding: 10px;
margin: 5px 0;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
}
.conversation-item:hover {
background-color: #f0f0f0;
}
.conversation-item.active {
background-color: #e3f2fd;
}
.conversation-title {
font-weight: bold;
margin-bottom: 5px;
padding-right: 25px;
}
.conversation-preview {
font-size: 0.9em;
color: #666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.delete-button {
position: absolute;
right: 5px;
top: 5px;
padding: 2px 6px;
background: #ff4444;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s;
}
.conversation-item:hover .delete-button {
opacity: 1;
}
#new-chat-button {
width: 100%;
margin-bottom: 10px;
background-color: #4CAF50;
}
#new-chat-button:hover {
background-color: #45a049;
}
.edit-icon {
margin-left: 5px;
opacity: 0;
cursor: pointer;
font-size: 14px;
color: #666;
transition: opacity 0.2s;
}
.conversation-item:hover .edit-icon {
opacity: 1;
}
.edit-title-input {
width: calc(100% - 30px);
padding: 4px 8px;
border: 1px solid #0070f3;
border-radius: 4px;
font-size: inherit;
font-weight: bold;
outline: none;
}
.edit-title-input:focus {
box-shadow: 0 0 0 2px rgba(0,112,243,0.2);
}
.import-export-buttons {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.import-export-buttons button {
flex: 1;
padding: 8px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.import-export-buttons button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<div id="app-container">
<div id="sidebar">
<button id="new-chat-button">新建对话</button>
<div class="import-export-buttons">
<button id="export-button" onclick="exportHistory()">导出对话</button>
<input type="file" id="import-file" accept=".json" style="display: none" onchange="importHistory(event)">
<button onclick="document.getElementById('import-file').click()">导入对话</button>
</div>
<div class="search-container">
<input type="text" id="search-input" placeholder="搜索对话...">
</div>
<div id="conversations-list"></div>
</div>
<div id="main-content">
<h1>DeepSeek R1 32B</h1>
<div id="chat-container"></div>
<div id="input-container">
<input type="text" id="message-input" placeholder="输入您的消息...">
<button id="send-button">发送</button>
</div>
</div>
</div>
<script>
// 工具函数
function showToast(message, type = 'info') {
Toastify({
text: message,
duration: 3000,
gravity: "top",
position: "right",
style: {
background: type === 'error' ? "#ff4444" : "#4CAF50"
}
}).showToast();
}
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
// 初始化变量
let currentConversationId = new URLSearchParams(window.location.search).get('id');
const chatContainer = document.getElementById('chat-container');
const messageInput = document.getElementById('message-input');
const sendButton = document.getElementById('send-button');
const searchInput = document.getElementById('search-input');
// 消息处理函数
function addMessage(text, type) {
const messageDiv = document.createElement('div');
messageDiv.className = 'message ' + (type === 'user' ? 'user-message' : 'ai-message');
if (type === 'ai') {
// 渲染 Markdown 内容
messageDiv.innerHTML = marked.parse(text);
} else {
messageDiv.textContent = text;
}
// 使用KaTeX自动渲染数学公式
if (typeof renderMathInElement === 'function') {
renderMathInElement(messageDiv, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false},
{left: "\\(", right: "\\)", display: false},
{left: "\\[", right: "\\]", display: true}
]
});
}
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// 对话列表管理
async function loadConversations() {
try {
const response = await fetch('/conversations');
const conversations = await response.json();
const listElement = document.getElementById('conversations-list');
listElement.innerHTML = '';
conversations.forEach(conv => {
const item = document.createElement('div');
item.className = 'conversation-item' + (conv.id === currentConversationId ? ' active' : '');
item.dataset.id = conv.id;
item.innerHTML = '<button class="delete-button" onclick="deleteConversation(\\'' + conv.id + '\\', event)">×</button>' +
'<div class="conversation-title" ondblclick="editTitle(\\'' + conv.id + '\\', this)">' +
(conv.title || '未命名对话') +
'<span class="edit-icon">✎</span></div>' +
'<div class="conversation-preview">' + (conv.lastMessage || '') + '</div>';
item.onclick = (e) => {
if (!e.target.classList.contains('delete-button') &&
!e.target.classList.contains('edit-icon')) {
loadConversation(conv.id);
}
};
listElement.appendChild(item);
});
} catch (error) {
console.error('加载对话列表失败:', error);
showToast('加载对话列表失败', 'error');
}
}
async function loadConversation(id) {
if (id === currentConversationId) return;
try {
currentConversationId = id;
const newUrl = \`\${window.location.pathname}?id=\${id}\`;
window.history.pushState({ id }, '', newUrl);
const response = await fetch(\`/history?id=\${id}\`);
const history = await response.json();
chatContainer.innerHTML = '';
if (history.messages) {
history.messages.forEach(msg => {
if (msg.role !== 'system') {
addMessage(msg.content, msg.role === 'user' ? 'user' : 'ai');
}
});
}
// 更新活动状态
document.querySelectorAll('.conversation-item').forEach(item => {
item.classList.toggle('active', item.dataset.id === id);
});
} catch (error) {
console.error('加载对话失败:', error);
showToast('加载对话失败', 'error');
}
}
async function createNewConversation() {
try {
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'new' })
});
const data = await response.json();
await loadConversation(data.conversationId);
loadConversations();
} catch (error) {
console.error('创建新对话失败:', error);
showToast('创建新对话失败', 'error');
}
}
async function deleteConversation(id, event) {
event.stopPropagation();
if (!confirm('确定要删除这个对话吗?')) return;
try {
await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete', conversationId: id })
});
if (id === currentConversationId) {
currentConversationId = null;
chatContainer.innerHTML = '';
window.history.pushState({}, '', window.location.pathname);
}
loadConversations();
showToast('对话已删除');
} catch (error) {
console.error('删除对话失败:', error);
showToast('删除对话失败', 'error');
}
}
// 修改标题编辑函数
async function editTitle(convId, element) {
// 在函数最外层定义 currentTitle,确保在所有闭包中都可访问
const currentTitle = element.textContent.trim().replace('✎', '').trim();
// 创建输入框
const input = document.createElement('input');
input.type = 'text';
input.className = 'edit-title-input';
input.value = currentTitle;
// 定义保存函数
const saveTitle = async () => {
const newTitle = input.value.trim();
if (!newTitle) {
showToast('标题不能为空', 'error');
element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
return;
}
if (newTitle !== currentTitle) {
try {
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'updateTitle',
conversationId: convId,
title: newTitle
})
});
if (!response.ok) throw new Error('更新失败');
showToast('标题已更新');
loadConversations();
} catch (error) {
console.error('更新标题失败:', error);
showToast('更新标题失败', 'error');
element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
}
} else {
element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
}
};
// 绑定事件
input.onblur = saveTitle;
input.onkeydown = (e) => { // 改用 onkeydown 更可靠
if (e.key === 'Enter') {
e.preventDefault();
input.blur();
} else if (e.key === 'Escape') {
element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
}
};
// 替换内容并聚焦
element.textContent = '';
element.appendChild(input);
input.focus();
input.select();
}
// 搜索功能
const handleSearch = debounce((query) => {
const items = document.querySelectorAll('.conversation-item');
items.forEach(item => {
const title = item.querySelector('.conversation-title').textContent;
const preview = item.querySelector('.conversation-preview').textContent;
const matches = title.toLowerCase().includes(query.toLowerCase()) ||
preview.toLowerCase().includes(query.toLowerCase());
item.style.display = matches ? 'block' : 'none';
});
}, 300);
// 发送消息
async function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
if (!currentConversationId) {
await createNewConversation();
}
messageInput.value = '';
addMessage(message, 'user');
const aiMessageDiv = document.createElement('div');
aiMessageDiv.className = 'message ai-message';
chatContainer.appendChild(aiMessageDiv);
let currentResponse = '';
try {
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, conversationId: currentConversationId })
});
if (!response.ok) throw new Error(\`HTTP error! status: \${response.status}\`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split('\\n').filter(line => line.trim() !== '');
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6);
if (jsonStr.trim() === '[DONE]') break;
try {
const data = JSON.parse(jsonStr);
if (data.response) {
currentResponse += data.response;
aiMessageDiv.innerHTML = marked.parse(currentResponse);
// 调用KaTeX自动渲染数学公式
if (typeof renderMathInElement === 'function') {
renderMathInElement(aiMessageDiv, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false},
{left: "\\(", right: "\\)", display: false},
{left: "\\[", right: "\\]", display: true}
]
});
}
chatContainer.scrollTop = chatContainer.scrollHeight;
}
} catch (e) {
console.error('解析JSON失败:', e);
}
}
}
}
// 更新对话列表
loadConversations();
} catch (error) {
console.error('发送消息失败:', error);
aiMessageDiv.innerHTML = marked.parse('Error: ' + error.message);
showToast('发送消息失败', 'error');
}
}
// 事件监听
document.getElementById('new-chat-button').onclick = createNewConversation;
sendButton.onclick = sendMessage;
messageInput.onkeypress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};
searchInput.oninput = (e) => handleSearch(e.target.value);
// 初始化
loadConversations();
if (currentConversationId) {
loadConversation(currentConversationId);
}
// 导出对话历史
async function exportHistory() {
try {
const response = await fetch('/conversations');
const conversations = await response.json();
// 获取每个对话的完整历史
const fullHistory = await Promise.all(
conversations.map(async conv => {
const historyResponse = await fetch(\`/history?id=\${conv.id}\`);
const history = await historyResponse.json();
return {
id: conv.id,
history: history
};
})
);
// 创建下载
const blob = new Blob([JSON.stringify(fullHistory, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = \`chat-history-\${new Date().toISOString().slice(0,10)}.json\`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('对话历史已导出');
} catch (error) {
console.error('导出失败:', error);
showToast('导出失败', 'error');
}
}
// 导入对话历史
async function importHistory(event) {
const file = event.target.files[0];
if (!file) return;
try {
const text = await file.text();
const histories = JSON.parse(text);
// 逐个导入对话
for (const item of histories) {
try {
// 创建新对话
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'new' })
});
const { conversationId } = await response.json();
// 保存对话历史
await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'import',
conversationId,
history: item.history
})
});
} catch (e) {
console.error('导入单个对话失败:', e);
}
}
// 刷新对话列表
loadConversations();
showToast('对话历史已导入');
} catch (error) {
console.error('导入失败:', error);
showToast('导入失败', 'error');
}
// 清除文件选择,允许重复导入同一文件
event.target.value = '';
}
</script>
</body>
</html>`;
// 添加密码验证中间件
async function checkAuth(request) {
const cookie = request.headers.get('cookie') || '';
const token = cookie.split(';')
.find(c => c.trim().startsWith('auth_token='))
?.split('=')[1];
return token === 'valid_token';
}
// 添加登录页面 HTML
const loginHtml = `<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 - DeepSeek R1</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
font-family: Arial, sans-serif;
}
.login-container {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 320px;
}
h1 {
text-align: center;
margin-bottom: 2rem;
}
input {
width: 100%;
padding: 0.5rem;
margin-bottom: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 0.75rem;
background-color: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0051a2;
}
.error {
color: red;
text-align: center;
margin-top: 1rem;
display: none;
}
</style>
</head>
<body>
<div class="login-container">
<h1>登录</h1>
<input type="password" id="password" placeholder="请输入密码">
<button onclick="login()">登录</button>
<div id="error" class="error"></div>
</div>
<script>
async function login() {
const password = document.getElementById('password').value;
const error = document.getElementById('error');
try {
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'login', password })
});
if (response.ok) {
window.location.href = '/';
} else {
error.textContent = '密码错误';
error.style.display = 'block';
}
} catch (e) {
error.textContent = '登录失败,请重试';
error.style.display = 'block';
}
}
// 支持回车键登录
document.getElementById('password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
login();
}
});
</script>
</body>
</html>`;