Merge branch 'master' of ssh://gitea.bowongai.com:222/bowong/mxivideo
This commit is contained in:
@@ -6,6 +6,7 @@ import EditorPage from './pages/EditorPage'
|
|||||||
import AIVideoPage from './pages/AIVideoPage'
|
import AIVideoPage from './pages/AIVideoPage'
|
||||||
import SettingsPage from './pages/SettingsPage'
|
import SettingsPage from './pages/SettingsPage'
|
||||||
import TemplateManagePage from './pages/TemplateManagePage'
|
import TemplateManagePage from './pages/TemplateManagePage'
|
||||||
|
import KVTestPage from './pages/KVTestPage'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -15,6 +16,7 @@ function App() {
|
|||||||
<Route path="/editor" element={<EditorPage />} />
|
<Route path="/editor" element={<EditorPage />} />
|
||||||
<Route path="/ai-video" element={<AIVideoPage />} />
|
<Route path="/ai-video" element={<AIVideoPage />} />
|
||||||
<Route path="/templates" element={<TemplateManagePage />} />
|
<Route path="/templates" element={<TemplateManagePage />} />
|
||||||
|
<Route path="/kv-test" element={<KVTestPage />} />
|
||||||
<Route path="/settings" element={<SettingsPage />} />
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ export const CloudflareKVDemo: React.FC = () => {
|
|||||||
const [inputKey, setInputKey] = useState('demo:test')
|
const [inputKey, setInputKey] = useState('demo:test')
|
||||||
const [inputValue, setInputValue] = useState('Hello, Cloudflare KV!')
|
const [inputValue, setInputValue] = useState('Hello, Cloudflare KV!')
|
||||||
const [searchPrefix, setSearchPrefix] = useState('demo:')
|
const [searchPrefix, setSearchPrefix] = useState('demo:')
|
||||||
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
|
|
||||||
|
// Add log function
|
||||||
|
const addLog = (message: string) => {
|
||||||
|
const timestamp = new Date().toLocaleTimeString()
|
||||||
|
const logMessage = `[${timestamp}] ${message}`
|
||||||
|
console.log(logMessage)
|
||||||
|
setLogs(prev => [logMessage, ...prev].slice(0, 20)) // Keep last 20 logs
|
||||||
|
}
|
||||||
|
|
||||||
// Use the general KV hook
|
// Use the general KV hook
|
||||||
const kv = useCloudflareKV()
|
const kv = useCloudflareKV()
|
||||||
@@ -31,9 +40,25 @@ export const CloudflareKVDemo: React.FC = () => {
|
|||||||
refresh: refreshCounter
|
refresh: refreshCounter
|
||||||
} = useKVValue<number>('demo:counter', true, true)
|
} = useKVValue<number>('demo:counter', true, true)
|
||||||
|
|
||||||
|
// Log initial state
|
||||||
|
React.useEffect(() => {
|
||||||
|
addLog('CloudflareKVDemo component initialized')
|
||||||
|
addLog(`KV loading state: ${kv.isLoading}`)
|
||||||
|
addLog(`KV has error: ${kv.hasError}`)
|
||||||
|
if (kv.hasError) {
|
||||||
|
addLog(`KV errors: ${JSON.stringify(kv.errors)}`)
|
||||||
|
}
|
||||||
|
}, [kv.isLoading, kv.hasError, kv.errors])
|
||||||
|
|
||||||
// Handle putting a value
|
// Handle putting a value
|
||||||
const handlePutValue = async () => {
|
const handlePutValue = async () => {
|
||||||
if (!inputKey || !inputValue) return
|
if (!inputKey || !inputValue) {
|
||||||
|
addLog('❌ Put operation failed: Key or value is empty')
|
||||||
|
alert('❌ Please enter both key and value')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addLog(`🔄 Starting put operation for key: ${inputKey}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data: DemoData = {
|
const data: DemoData = {
|
||||||
@@ -42,49 +67,77 @@ export const CloudflareKVDemo: React.FC = () => {
|
|||||||
counter: (typeof counterValue === 'number' ? counterValue : 0) + 1
|
counter: (typeof counterValue === 'number' ? counterValue : 0) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
await kv.put(inputKey, data)
|
addLog(`📤 Sending data: ${JSON.stringify(data)}`)
|
||||||
|
const result = await kv.put(inputKey, data)
|
||||||
|
addLog(`✅ Put operation successful: ${JSON.stringify(result)}`)
|
||||||
alert('✅ Value stored successfully!')
|
alert('✅ Value stored successfully!')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('❌ Failed to store value: ' + (error as Error).message)
|
const errorMessage = (error as Error).message
|
||||||
|
addLog(`❌ Put operation failed: ${errorMessage}`)
|
||||||
|
alert('❌ Failed to store value: ' + errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle getting a value
|
// Handle getting a value
|
||||||
const handleGetValue = async () => {
|
const handleGetValue = async () => {
|
||||||
if (!inputKey) return
|
if (!inputKey) {
|
||||||
|
addLog('❌ Get operation failed: Key is empty')
|
||||||
|
alert('❌ Please enter a key')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addLog(`🔄 Starting get operation for key: ${inputKey}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await kv.get<DemoData>(inputKey)
|
const result = await kv.get<DemoData>(inputKey)
|
||||||
if (result) {
|
if (result) {
|
||||||
|
addLog(`✅ Get operation successful: ${JSON.stringify(result)}`)
|
||||||
alert(`✅ Retrieved value: ${JSON.stringify(result, null, 2)}`)
|
alert(`✅ Retrieved value: ${JSON.stringify(result, null, 2)}`)
|
||||||
} else {
|
} else {
|
||||||
|
addLog(`ℹ️ Get operation completed: Key not found`)
|
||||||
alert('ℹ️ Key not found')
|
alert('ℹ️ Key not found')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('❌ Failed to get value: ' + (error as Error).message)
|
const errorMessage = (error as Error).message
|
||||||
|
addLog(`❌ Get operation failed: ${errorMessage}`)
|
||||||
|
alert('❌ Failed to get value: ' + errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle deleting a value
|
// Handle deleting a value
|
||||||
const handleDeleteValue = async () => {
|
const handleDeleteValue = async () => {
|
||||||
if (!inputKey) return
|
if (!inputKey) {
|
||||||
|
addLog('❌ Delete operation failed: Key is empty')
|
||||||
|
alert('❌ Please enter a key')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addLog(`🔄 Starting delete operation for key: ${inputKey}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await kv.delete(inputKey)
|
const result = await kv.delete(inputKey)
|
||||||
|
addLog(`✅ Delete operation successful: ${JSON.stringify(result)}`)
|
||||||
alert('✅ Value deleted successfully!')
|
alert('✅ Value deleted successfully!')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('❌ Failed to delete value: ' + (error as Error).message)
|
const errorMessage = (error as Error).message
|
||||||
|
addLog(`❌ Delete operation failed: ${errorMessage}`)
|
||||||
|
alert('❌ Failed to delete value: ' + errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle listing keys
|
// Handle listing keys
|
||||||
const handleListKeys = async () => {
|
const handleListKeys = async () => {
|
||||||
|
addLog(`🔄 Starting list keys operation with prefix: ${searchPrefix}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await kv.listKeys(searchPrefix, 50)
|
const result = await kv.listKeys(searchPrefix, 50)
|
||||||
|
addLog(`✅ List keys operation successful: Found ${result.result.keys.length} keys`)
|
||||||
const keyNames = result.result.keys.map(k => k.name).join('\n')
|
const keyNames = result.result.keys.map(k => k.name).join('\n')
|
||||||
alert(`✅ Found keys:\n${keyNames || 'No keys found'}`)
|
alert(`✅ Found keys:\n${keyNames || 'No keys found'}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('❌ Failed to list keys: ' + (error as Error).message)
|
const errorMessage = (error as Error).message
|
||||||
|
addLog(`❌ List keys operation failed: ${errorMessage}`)
|
||||||
|
alert('❌ Failed to list keys: ' + errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,9 +330,33 @@ export const CloudflareKVDemo: React.FC = () => {
|
|||||||
>
|
>
|
||||||
Clear All States
|
Clear All States
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setLogs([])}
|
||||||
|
className="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition-colors ml-3"
|
||||||
|
>
|
||||||
|
Clear Logs
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Debug Logs Section */}
|
||||||
|
<div className="bg-white rounded-lg shadow-lg p-6 mt-6">
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">
|
||||||
|
Debug Logs
|
||||||
|
</h2>
|
||||||
|
<div className="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm max-h-96 overflow-y-auto">
|
||||||
|
{logs.length === 0 ? (
|
||||||
|
<div className="text-gray-500">No logs yet...</div>
|
||||||
|
) : (
|
||||||
|
logs.map((log, index) => (
|
||||||
|
<div key={index} className="mb-1">
|
||||||
|
{log}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Plus, FolderOpen, Video, Music, Zap, TestTube, Sparkles } from 'lucide-react'
|
import { Plus, FolderOpen, Video, Music, Zap, TestTube, Sparkles, Database } from 'lucide-react'
|
||||||
import { TauriService } from '../services/tauri'
|
import { TauriService } from '../services/tauri'
|
||||||
import { useProjectStore } from '../stores/useProjectStore'
|
import { useProjectStore } from '../stores/useProjectStore'
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ const HomePage: React.FC = () => {
|
|||||||
{ icon: Music, label: '音频处理', description: '处理音频文件,添加效果', path: '/audio' },
|
{ icon: Music, label: '音频处理', description: '处理音频文件,添加效果', path: '/audio' },
|
||||||
{ icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑', path: '/editor' },
|
{ icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑', path: '/editor' },
|
||||||
{ icon: FolderOpen, label: '导入媒体', description: '导入视频、音频和图片文件', path: '/media' },
|
{ icon: FolderOpen, label: '导入媒体', description: '导入视频、音频和图片文件', path: '/media' },
|
||||||
|
{ icon: Database, label: 'KV 存储测试', description: '测试 Cloudflare KV 键值存储功能', path: '/kv-test' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const testTauriConnection = async () => {
|
const testTauriConnection = async () => {
|
||||||
|
|||||||
74
src/pages/KVTestPage.tsx
Normal file
74
src/pages/KVTestPage.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { ArrowLeft } from 'lucide-react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import CloudflareKVDemo from '../components/CloudflareKVDemo'
|
||||||
|
|
||||||
|
const KVTestPage: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-secondary-50">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="bg-white border-b border-secondary-200 px-6 py-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="flex items-center space-x-2 text-secondary-600 hover:text-secondary-900 transition-colors"
|
||||||
|
>
|
||||||
|
<ArrowLeft size={20} />
|
||||||
|
<span>返回首页</span>
|
||||||
|
</Link>
|
||||||
|
<div className="h-6 w-px bg-secondary-300" />
|
||||||
|
<h1 className="text-2xl font-bold text-secondary-900">
|
||||||
|
Cloudflare KV 测试
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Description */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border border-secondary-200 p-6 mb-6">
|
||||||
|
<h2 className="text-lg font-semibold text-secondary-900 mb-3">
|
||||||
|
关于 Cloudflare KV 测试
|
||||||
|
</h2>
|
||||||
|
<p className="text-secondary-600 mb-4">
|
||||||
|
这个页面演示了如何在 MixVideo V2 中使用 Cloudflare KV 存储。
|
||||||
|
您可以测试键值对的存储、读取、删除和批量操作功能。
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
<div className="bg-blue-50 p-4 rounded-lg">
|
||||||
|
<h3 className="font-medium text-blue-900 mb-2">存储数据</h3>
|
||||||
|
<p className="text-sm text-blue-700">
|
||||||
|
将键值对数据存储到 Cloudflare KV
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-green-50 p-4 rounded-lg">
|
||||||
|
<h3 className="font-medium text-green-900 mb-2">读取数据</h3>
|
||||||
|
<p className="text-sm text-green-700">
|
||||||
|
根据键名获取存储的数据
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-purple-50 p-4 rounded-lg">
|
||||||
|
<h3 className="font-medium text-purple-900 mb-2">列出键名</h3>
|
||||||
|
<p className="text-sm text-purple-700">
|
||||||
|
按前缀搜索和列出所有键名
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-orange-50 p-4 rounded-lg">
|
||||||
|
<h3 className="font-medium text-orange-900 mb-2">批量操作</h3>
|
||||||
|
<p className="text-sm text-orange-700">
|
||||||
|
一次性处理多个键值对操作
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* KV Demo Component */}
|
||||||
|
<CloudflareKVDemo />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default KVTestPage
|
||||||
@@ -59,7 +59,8 @@ export class CloudflareKVClient {
|
|||||||
async put(key: string, value: any, metadata?: Record<string, any>): Promise<CloudflareKVResponse> {
|
async put(key: string, value: any, metadata?: Record<string, any>): Promise<CloudflareKVResponse> {
|
||||||
try {
|
try {
|
||||||
const url = `${this.getKVBaseUrl()}/values/${encodeURIComponent(key)}`
|
const url = `${this.getKVBaseUrl()}/values/${encodeURIComponent(key)}`
|
||||||
|
console.log(`[CloudflareKV] PUT request to: ${url}`)
|
||||||
|
|
||||||
const body: any = {
|
const body: any = {
|
||||||
value: typeof value === 'string' ? value : JSON.stringify(value)
|
value: typeof value === 'string' ? value : JSON.stringify(value)
|
||||||
}
|
}
|
||||||
@@ -68,6 +69,8 @@ export class CloudflareKVClient {
|
|||||||
body.metadata = JSON.stringify(metadata)
|
body.metadata = JSON.stringify(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[CloudflareKV] PUT body:`, body)
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -77,8 +80,11 @@ export class CloudflareKVClient {
|
|||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log(`[CloudflareKV] PUT response status: ${response.status}`)
|
||||||
|
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
|
console.log(`[CloudflareKV] PUT response:`, result)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}: ${result.errors?.[0]?.message || 'Unknown error'}`)
|
throw new Error(`HTTP ${response.status}: ${result.errors?.[0]?.message || 'Unknown error'}`)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user