230 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import React, { useState, useEffect, useCallback } from 'react'
interface VersionInfo {
version: string
download_url: string
checksum: string
}
export default function UploadPage() {
const [currentVersion, setCurrentVersion] = useState<VersionInfo | null>(null)
const [newVersion, setNewVersion] = useState('')
const [versionFile, setVersionFile] = useState<File | null>(null)
const [versionFileHash, setVersionFileHash] = useState('')
const [isUploading, setIsUploading] = useState(false)
const [uploadError, setUploadError] = useState('')
const [uploadSuccess, setUploadSuccess] = useState(false)
const canUploadVersion = newVersion && versionFile && !isUploading
// 计算文件 SHA-256
const calculateSha256 = async (file: File): Promise<string> => {
const buffer = await file.arrayBuffer()
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
}
// 获取当前版本信息
const fetchCurrentVersion = useCallback(async () => {
try {
const response = await fetch('/api/api/version')
if (response.ok) {
const data: VersionInfo = await response.json()
setCurrentVersion(data)
}
} catch (err) {
console.error('获取版本信息失败:', err)
}
}, [])
// 处理版本文件选择
const handleVersionFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const input = event.target
if (input.files && input.files[0]) {
const file = input.files[0]
setVersionFile(file)
setVersionFileHash(await calculateSha256(file))
}
}
// 清除表单
const resetForm = () => {
setNewVersion('')
setVersionFile(null)
setVersionFileHash('')
setUploadError('')
}
// 上传新版本
const uploadVersion = async () => {
if (!versionFile || !newVersion) return
setIsUploading(true)
setUploadError('')
setUploadSuccess(false)
const formData = new FormData()
formData.append('file', versionFile)
formData.append('version', newVersion)
try {
const response = await fetch('/api/upload/version', {
method: 'POST',
body: formData
})
if (!response.ok) {
throw new Error('Upload failed')
}
await fetchCurrentVersion()
setUploadSuccess(true)
resetForm()
setTimeout(() => {
setUploadSuccess(false)
}, 3000)
} catch (err) {
console.error('上传失败:', err)
setUploadError('上传失败,请重试')
} finally {
setIsUploading(false)
}
}
useEffect(() => {
fetchCurrentVersion()
}, [fetchCurrentVersion])
return (
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-3xl font-bold text-gray-800 mb-8"></h1>
{/* 当前版本信息卡片 */}
<div className="bg-white rounded-lg shadow-md mb-8 overflow-hidden">
<div className="bg-gray-50 px-6 py-4 border-b">
<h2 className="text-xl font-semibold text-gray-700"></h2>
</div>
<div className="p-6">
{currentVersion ? (
<div className="space-y-3">
<div className="grid grid-cols-12 gap-4 items-center">
<span className="text-gray-600 col-span-2">:</span>
<span className="font-medium text-gray-800 col-span-10">{currentVersion.version}</span>
</div>
<div className="grid grid-cols-12 gap-4 items-center">
<span className="text-gray-600 col-span-2">:</span>
<a
href={currentVersion.download_url}
className="text-blue-600 hover:text-blue-800 break-all col-span-10"
>
{currentVersion.download_url}
</a>
</div>
<div className="grid grid-cols-12 gap-4 items-center">
<span className="text-gray-600 col-span-2">:</span>
<span className="font-mono text-sm text-gray-700 break-all col-span-10">
{currentVersion.checksum}
</span>
</div>
</div>
) : (
<div className="text-gray-500 italic"></div>
)}
</div>
</div>
{/* 上传新版本表单 */}
<div className="bg-white rounded-lg shadow-md">
<div className="bg-gray-50 px-6 py-4 border-b">
<h2 className="text-xl font-semibold text-gray-700"></h2>
</div>
<div className="p-6 space-y-6">
{/* 版本号输入 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
value={newVersion}
onChange={(e) => setNewVersion(e.target.value)}
type="text"
className="w-full px-4 py-2 border rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition"
placeholder="例如: 1.0.0"
/>
</div>
{/* 文件上传 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<div className="mt-1">
<div className="border-2 border-dashed border-gray-300 rounded-lg px-6 py-8">
<div className="text-center">
<input
type="file"
onChange={handleVersionFileChange}
className="hidden"
id="file-upload"
/>
<label
htmlFor="file-upload"
className="cursor-pointer inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
</label>
<p className="mt-2 text-sm text-gray-600">
{versionFile?.name || '未选择文件'}
</p>
</div>
{versionFileHash && (
<div className="mt-4 text-center">
<p className="text-xs text-gray-500">SHA-256 :</p>
<p className="font-mono text-xs text-gray-600 break-all">
{versionFileHash}
</p>
</div>
)}
</div>
</div>
</div>
{/* 错误提示 */}
{uploadError && (
<div className="text-red-600 text-sm">
{uploadError}
</div>
)}
{/* 成功提示 */}
{uploadSuccess && (
<div className="bg-green-50 text-green-800 px-4 py-2 rounded-md text-sm">
</div>
)}
{/* 提交按钮 */}
<div className="flex justify-end">
<button
onClick={uploadVersion}
disabled={!canUploadVersion}
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors"
>
{isUploading && (
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
{isUploading ? '上传中...' : '上传新版本'}
</button>
</div>
</div>
</div>
</div>
)
}