Final push for project

Change-Id: I9103078156eca93df2482b9fe3854d9301bb98b3
diff --git a/frontend/my-app/package-lock.json b/frontend/my-app/package-lock.json
index 21a1a55..533fd75 100644
--- a/frontend/my-app/package-lock.json
+++ b/frontend/my-app/package-lock.json
@@ -13,6 +13,7 @@
         "@mui/icons-material": "^7.0.2",
         "@mui/material": "^7.0.2",
         "axios": "^1.9.0",
+        "file-saver": "^2.0.5",
         "react": "^19.0.0",
         "react-dom": "^19.0.0",
         "react-router-dom": "^7.5.1"
@@ -4915,6 +4916,12 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
+      "license": "MIT"
+    },
     "node_modules/fill-range": {
       "version": "7.1.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
diff --git a/frontend/my-app/package.json b/frontend/my-app/package.json
index 07b11c9..04db654 100644
--- a/frontend/my-app/package.json
+++ b/frontend/my-app/package.json
@@ -18,6 +18,7 @@
     "@mui/icons-material": "^7.0.2",
     "@mui/material": "^7.0.2",
     "axios": "^1.9.0",
+    "file-saver": "^2.0.5",
     "react": "^19.0.0",
     "react-dom": "^19.0.0",
     "react-router-dom": "^7.5.1"
diff --git a/frontend/my-app/src/App.jsx b/frontend/my-app/src/App.jsx
index 214e127..c83b97d 100644
--- a/frontend/my-app/src/App.jsx
+++ b/frontend/my-app/src/App.jsx
@@ -8,6 +8,7 @@
 import UserProfile from './pages/UserProfile';
 import UserContextProvider from './contexts/UserContext';
 import TorrentUploadPage from './pages/UploadTorrent';
+import LoginPage from './pages/Login';
 function App() {
   return (
     <UserContextProvider>
@@ -18,6 +19,7 @@
             <Route path="/" element={<Home />} />
             <Route path="/user/:id" element={<UserProfile />} />
              <Route path="/Upload" element={<TorrentUploadPage />} />
+              <Route path="/login" element={<LoginPage />} />
           </Routes>
         </div>
       </ThemeProvider>
diff --git a/frontend/my-app/src/components/Button/TorrentDownloadButton.jsx b/frontend/my-app/src/components/Button/TorrentDownloadButton.jsx
new file mode 100644
index 0000000..a8a6177
--- /dev/null
+++ b/frontend/my-app/src/components/Button/TorrentDownloadButton.jsx
@@ -0,0 +1,84 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import axios from 'axios';
+import { saveAs } from 'file-saver';
+import Button from '@mui/material/Button';
+import LinearProgress from '@mui/material/LinearProgress';
+import Box from '@mui/material/Box';
+import Typography from '@mui/material/Typography';
+
+function TorrentDownloadButton({ infoHash, title }) {
+  // 新增状态:下载进度(0~100)
+  const [progress, setProgress] = useState(0);
+  // 新增状态:是否正在下载
+  const [downloading, setDownloading] = useState(false);
+
+  const handleDownload = async () => {
+    try {
+      setDownloading(true);
+      setProgress(0);
+      // 1) 启动下载
+      await axios.post('/api/torrents/download', null, { params: { infoHash } });
+
+      // 2) 轮询进度
+      let p = 0;
+      while (p < 1) {
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        const res = await axios.get(`/api/torrents/download/status/${infoHash}`);
+        p = res.data;
+        setProgress(Math.round(p * 100));
+      }
+
+      // 3) 下载文件流
+      const fileRes = await axios.get(
+        `/api/torrents/download/file/${infoHash}`,
+        { responseType: 'blob' }
+      );
+
+      // 4) 保存文件
+      saveAs(fileRes.data, title);
+    } catch (err) {
+      console.error('下载过程中出错', err);
+    } finally {
+      setDownloading(false);
+    }
+  };
+
+  return (
+    <Box sx={{ width: 200 }}>
+      <Button
+        variant="outlined"
+        color="success"
+        onClick={handleDownload}
+        disabled={downloading}
+        sx={{
+          width: '100%',
+          fontSize: '0.85rem',
+          borderColor: '#4caf50',
+          '&:hover': {
+            backgroundColor: 'rgba(76, 175, 80, 0.15)',
+            borderColor: '#388e3c',
+          },
+        }}
+      >
+        {downloading ? `下载中 ${progress}%` : '下载'}
+      </Button>
+
+      {downloading && (
+        <Box sx={{ mt: 1 }}>
+          <LinearProgress variant="determinate" value={progress} />
+          <Typography variant="caption" display="block" align="center">
+            {progress}%
+          </Typography>
+        </Box>
+      )}
+    </Box>
+  );
+}
+
+TorrentDownloadButton.propTypes = {
+  infoHash: PropTypes.string.isRequired,
+  title: PropTypes.string.isRequired,
+};
+
+export default TorrentDownloadButton;
diff --git a/frontend/my-app/src/pages/Home.jsx b/frontend/my-app/src/pages/Home.jsx
index 6bca4b8..e2f7f8e 100644
--- a/frontend/my-app/src/pages/Home.jsx
+++ b/frontend/my-app/src/pages/Home.jsx
@@ -1,12 +1,10 @@
 import React, { useState, useEffect } from 'react';
 import {
   Box, Container, Typography, Paper,
-  List, ListItem, ListItemText, Chip, Stack, CircularProgress, Alert
+  List, ListItem, ListItemText, Chip, Stack, CircularProgress, Alert, Button
 } from '@mui/material';
 import { Link } from 'react-router-dom'; // 假设你已经配置了 React Router
 
-// import '../styles/base/base.css'; // ✅ 引入统一样式 - 我们将尝试用 sx prop 替代
-
 // 模拟从API获取数据
 const fetchTorrents = () => {
   return new Promise((resolve) => {
@@ -63,7 +61,7 @@
 
   return (
     <Box sx={{ minHeight: '100vh', py: 4, background: 'linear-gradient(135deg, #2c3e50, #4ca1af)', color: 'white' }}>
-      <Container maxWidth="md" sx={{ position: 'relative', zIndex: 10 }}> {/* Changed to md for wider content */}
+      <Container maxWidth="md" sx={{ position: 'relative', zIndex: 10 }}>
         <Typography variant="h4" sx={{ textAlign: 'center', mb: 3, fontWeight: 'bold' }}>
           🌐 首页 · Mini-Tracker
         </Typography>
@@ -90,36 +88,41 @@
                   key={torrent.id}
                   component={Link} // react-router-dom Link
                   to={`/detail/${torrent.id}`} // 假设的详情页路由
-                  sx={{ 
-                    color: 'white', 
+                  sx={{
+                    color: 'white',
                     textDecoration: 'none',
-                    mb: 1.5, // 增加列表项间距
-                    p: 1.5, // 增加列表项内边距
+                    mb: 1.5,
+                    p: 1.5,
                     borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
                     '&:last-child': {
-                        borderBottom: 'none' // 移除最后一个元素的边框
+                      borderBottom: 'none'
                     },
-                    display: 'flex', // 确保内部元素正确对齐
-                    flexDirection: { xs: 'column', sm: 'row' }, // 响应式布局
+                    display: 'flex',
+                    flexDirection: { xs: 'column', sm: 'row' },
                     alignItems: { xs: 'flex-start', sm: 'center' }
                   }}
-                  divider={false} // 使用自定义边框代替
+                  divider={false}
                 >
                   <ListItemText
                     primary={torrent.title}
                     secondary={`大小: ${torrent.size} · 上传者: ${torrent.uploader}`}
-                    primaryTypographyProps={{ variant: 'h6', component: 'div', sx: { mb: 0.5, color: '#f5f5f5', fontWeight: 500 } }}
+                    primaryTypographyProps={{
+                      variant: 'h6',
+                      component: 'div',
+                      sx: { mb: 0.5, color: '#f5f5f5', fontWeight: 500 }
+                    }}
                     secondaryTypographyProps={{ sx: { color: '#bbb', fontSize: '0.85rem' } }}
-                    sx={{ flexGrow: 1, mb: { xs: 1, sm: 0 }, mr: { sm: 2 } }} // 响应式边距
+                    sx={{ flexGrow: 1, mb: { xs: 1, sm: 0 }, mr: { sm: 2 } }}
                   />
-                  <Stack direction="row" spacing={1} sx={{ flexWrap: 'wrap', gap: 0.5 }}> {/* 允许标签换行 */}
+
+                  <Stack direction="row" spacing={1} sx={{ flexWrap: 'wrap', gap: 0.5 }}>
                     {torrent.tags.map(tag => (
                       <Chip
                         key={tag}
                         label={tag}
                         size="small"
-                        sx={{ 
-                          backgroundColor: 'rgba(255, 255, 255, 0.2)', // upload-chip 样式
+                        sx={{
+                          backgroundColor: 'rgba(255, 255, 255, 0.2)',
                           color: 'white',
                           cursor: 'pointer',
                           '&:hover': {
@@ -129,6 +132,29 @@
                       />
                     ))}
                   </Stack>
+
+                  {/* ——— 在此处添加“下载”按钮 —— */}
+                  <Button
+                    variant="outlined"
+                    component="a"
+                    href={`http://localhost:8080/api/downloads/${torrent.id}/${encodeURIComponent(torrent.title)}.torrent`}
+                    download={`${torrent.title}.torrent`}
+                    sx={{
+                      ml: { xs: 0, sm: 2 }, // 小屏幕时居中;大屏幕时与右边距留空
+                      mt: { xs: 1, sm: 0 }, // 小屏时在下方,留一点间距
+                      color: '#4caf50',
+                      borderColor: '#4caf50',
+                      fontSize: '0.85rem',
+                      '&:hover': {
+                        backgroundColor: 'rgba(76, 175, 80, 0.15)',
+                        borderColor: '#388e3c'
+                      }
+                    }}
+                  >
+                    下载
+                  </Button>
+                  {/* ——— 下载按钮结束 ——— */}
+
                 </ListItem>
               ))}
             </List>
@@ -136,7 +162,7 @@
         </Paper>
       </Container>
 
-      {/* 背景泡泡动画复用 - 这些类名 (.bubbles, .bubble) 和 @keyframes rise 应该在全局CSS或者下面的 <style> 标签中定义 */}
+      {/* 背景泡泡动画复用 */}
       <Box className="bubbles" sx={{
          pointerEvents: 'none',
          position: 'fixed',
@@ -145,7 +171,7 @@
          width: '100%',
          height: '100%',
          overflow: 'hidden',
-         zIndex: 1, 
+         zIndex: 1,
       }}>
         {[...Array(40)].map((_, i) => (
           <Box
@@ -153,48 +179,43 @@
             className="bubble"
             sx={{
               position: 'absolute',
-              bottom: '-150px', // 确保从屏幕外开始
-              background: `hsla(${Math.random() * 360}, 70%, 80%, 0.15)`, // 增加颜色多样性
+              bottom: '-150px',
+              background: `hsla(${Math.random() * 360}, 70%, 80%, 0.15)`,
               borderRadius: '50%',
               animation: 'rise 20s infinite ease-in',
-              width: `${Math.random() * 25 + 10}px`, // 调整大小范围
+              width: `${Math.random() * 25 + 10}px`,
               height: `${Math.random() * 25 + 10}px`,
               left: `${Math.random() * 100}%`,
-              animationDuration: `${15 + Math.random() * 20}s`, // 调整动画时长
-              animationDelay: `${Math.random() * 10}s`, // 调整动画延迟
-              opacity: 0, // 初始透明
+              animationDuration: `${15 + Math.random() * 20}s`,
+              animationDelay: `${Math.random() * 10}s`,
+              opacity: 0,
             }}
           />
         ))}
       </Box>
-      {/* 定义动画和其他全局可能需要的样式 */}
       <style>
         {`
-          body { /* 基本重置 */
+          body {
             margin: 0;
-            font-family: 'Roboto', sans-serif; /* 确保字体一致 */
+            font-family: 'Roboto', sans-serif;
           }
 
           @keyframes rise {
             0% {
               transform: translateY(0) scale(0.8);
-              opacity: 0; /* 从透明开始 */
+              opacity: 0;
             }
             10% {
-                opacity: 1; /* 渐显 */
+              opacity: 1;
             }
             90% {
-                opacity: 1; /* 保持可见 */
+              opacity: 1;
             }
             100% {
-              transform: translateY(-130vh) scale(0.3); /* 飘得更高更小 */
-              opacity: 0; /* 渐隐 */
+              transform: translateY(-130vh) scale(0.3);
+              opacity: 0;
             }
           }
-
-          /* 如果 .bubbles 和 .bubble 样式没有在 sx 中完全覆盖,可以在这里补充 */
-          /* .bubbles { ... } */
-          /* .bubble { ... } */
         `}
       </style>
     </Box>
@@ -202,4 +223,3 @@
 }
 
 export default Home;
-
diff --git a/frontend/my-app/src/pages/Login.jsx b/frontend/my-app/src/pages/Login.jsx
new file mode 100644
index 0000000..19f13ca
--- /dev/null
+++ b/frontend/my-app/src/pages/Login.jsx
@@ -0,0 +1,214 @@
+import React, { useState } from 'react';
+import {
+  Box, Container, Typography, Paper, TextField, Button, CircularProgress, Alert
+} from '@mui/material';
+// import { useNavigate } from 'react-router-dom'; // 如果需要重定向,请取消注释
+
+function LoginPage() {
+  const [username, setUsername] = useState('');
+  const [password, setPassword] = useState('');
+  const [loading, setLoading] = useState(false);
+  const [message, setMessage] = useState(null); // 用于显示成功或错误消息
+  const [messageSeverity, setMessageSeverity] = useState('info'); // 'success', 'error', 'info'
+
+  // const navigate = useNavigate(); // 如果需要重定向,请取消注释
+
+  const handleSubmit = async (event) => {
+    event.preventDefault(); // 阻止表单默认提交行为
+
+    setMessage(null); // 清除之前的消息
+    setLoading(true); // 显示加载指示器
+
+    try {
+      const response = await fetch('http://localhost:8080/api/auth/login', { // 确保这里的URL与您的后端接口一致
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify({ username, password })
+      });
+
+      const data = await response.json();
+
+      if (response.ok) {
+        // 登录成功
+        setMessage('登录成功!欢迎 ' + data.username);
+        setMessageSeverity('success');
+        console.log('登录成功:', data);
+        // 将 JWT Token 保存到 localStorage
+        localStorage.setItem('jwt_token', data.token);
+        // 可选:登录成功后重定向到其他页面
+        // navigate('/dashboard'); 
+      } else {
+        // 登录失败
+        const errorMessage = data.error || '未知错误';
+        setMessage('登录失败: ' + errorMessage);
+        setMessageSeverity('error');
+        console.error('登录失败:', data);
+      }
+    } catch (error) {
+      // 网络错误或请求发送失败
+      setMessage('网络错误,请稍后再试。');
+      setMessageSeverity('error');
+      console.error('请求发送失败:', error);
+    } finally {
+      setLoading(false); // 隐藏加载指示器
+    }
+  };
+
+  return (
+    <Box sx={{
+      minHeight: '100vh',
+      display: 'flex',
+      justifyContent: 'center',
+      alignItems: 'center',
+      background: 'linear-gradient(135deg, #2c3e50, #4ca1af)', // 与 Home 组件相同的背景
+      color: 'white',
+      py: 4 // 垂直内边距
+    }}>
+      <Container maxWidth="xs" sx={{ position: 'relative', zIndex: 10 }}>
+        <Typography variant="h4" align="center" sx={{ mb: 4, fontWeight: 'bold' }}>
+          🔐 用户登录
+        </Typography>
+
+        <Paper sx={{
+          p: 4, // 内边距
+          backgroundColor: 'rgba(30, 30, 30, 0.9)', // 半透明深色背景
+          borderRadius: '12px', // 圆角
+          boxShadow: '0 8px 16px rgba(0,0,0,0.3)' // 阴影
+        }}>
+          {message && (
+            <Alert severity={messageSeverity} sx={{ mb: 2, borderRadius: '8px' }}>
+              {message}
+            </Alert>
+          )}
+
+          <form onSubmit={handleSubmit}>
+            <TextField
+              label="用户名"
+              variant="outlined"
+              fullWidth
+              margin="normal"
+              value={username}
+              onChange={(e) => setUsername(e.target.value)}
+              required
+              sx={{
+                mb: 2, // 底部外边距
+                '& .MuiOutlinedInput-root': {
+                  borderRadius: '8px',
+                  color: 'white', // 输入文字颜色
+                  '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.3)' }, // 边框颜色
+                  '&:hover fieldset': { borderColor: 'rgba(255, 255, 255, 0.5)' },
+                  '&.Mui-focused fieldset': { borderColor: '#8b5cf6' }, // 聚焦时边框颜色
+                },
+                '& .MuiInputLabel-root': { color: '#bbb' }, // 标签颜色
+                '& .MuiInputLabel-root.Mui-focused': { color: '#8b5cf6' }, // 聚焦时标签颜色
+              }}
+            />
+            <TextField
+              label="密码"
+              type="password"
+              variant="outlined"
+              fullWidth
+              margin="normal"
+              value={password}
+              onChange={(e) => setPassword(e.target.value)}
+              required
+              sx={{
+                mb: 3, // 底部外边距
+                '& .MuiOutlinedInput-root': {
+                  borderRadius: '8px',
+                  color: 'white', // 输入文字颜色
+                  '& fieldset': { borderColor: 'rgba(255, 255, 255, 0.3)' },
+                  '&:hover fieldset': { borderColor: 'rgba(255, 255, 255, 0.5)' },
+                  '&.Mui-focused fieldset': { borderColor: '#8b5cf6' },
+                },
+                '& .MuiInputLabel-root': { color: '#bbb' },
+                '& .MuiInputLabel-root.Mui-focused': { color: '#8b5cf6' },
+              }}
+            />
+            <Button
+              type="submit"
+              variant="contained"
+              fullWidth
+              disabled={loading}
+              sx={{
+                py: '0.75rem', // 垂直内边距
+                backgroundImage: 'linear-gradient(to right, #6366f1, #8b5cf6)', // 渐变背景
+                color: 'white',
+                borderRadius: '0.375rem', // 圆角
+                fontWeight: 600,
+                position: 'relative', // 用于加载指示器定位
+                '&:hover': {
+                  backgroundImage: 'linear-gradient(to right, #4f46e5, #7c3aed)',
+                },
+              }}
+            >
+              {loading ? <CircularProgress size={24} color="inherit" sx={{ position: 'absolute' }} /> : '登录'}
+            </Button>
+          </form>
+        </Paper>
+      </Container>
+
+      {/* 背景泡泡动画 - 复用 Home 组件中的样式 */}
+      <Box className="bubbles" sx={{
+          pointerEvents: 'none',
+          position: 'fixed',
+          top: 0,
+          left: 0,
+          width: '100%',
+          height: '100%',
+          overflow: 'hidden',
+          zIndex: 1, 
+      }}>
+        {[...Array(40)].map((_, i) => (
+          <Box
+            key={i}
+            className="bubble"
+            sx={{
+              position: 'absolute',
+              bottom: '-150px', // 确保从屏幕外开始
+              background: `hsla(${Math.random() * 360}, 70%, 80%, 0.15)`, // 增加颜色多样性
+              borderRadius: '50%',
+              animation: 'rise 20s infinite ease-in',
+              width: `${Math.random() * 25 + 10}px`, // 调整大小范围
+              height: `${Math.random() * 25 + 10}px`,
+              left: `${Math.random() * 100}%`,
+              animationDuration: `${15 + Math.random() * 20}s`, // 调整动画时长
+              animationDelay: `${Math.random() * 10}s`, // 调整动画延迟
+              opacity: 0, // 初始透明
+            }}
+          />
+        ))}
+      </Box>
+      {/* 定义动画和其他全局可能需要的样式 */}
+      <style>
+        {`
+          body { /* 基本重置 */
+            margin: 0;
+            font-family: 'Roboto', sans-serif; /* 确保字体一致 */
+          }
+
+          @keyframes rise {
+            0% {
+              transform: translateY(0) scale(0.8);
+              opacity: 0; /* 从透明开始 */
+            }
+            10% {
+                opacity: 1; /* 渐显 */
+            }
+            90% {
+                opacity: 1; /* 保持可见 */
+            }
+            100% {
+              transform: translateY(-130vh) scale(0.3); /* 飘得更高更小 */
+              opacity: 0; /* 渐隐 */
+            }
+          }
+        `}
+      </style>
+    </Box>
+  );
+}
+
+export default LoginPage;
\ No newline at end of file
diff --git a/frontend/my-app/src/pages/UploadTorrent.jsx b/frontend/my-app/src/pages/UploadTorrent.jsx
index 493cdce..465afda 100644
--- a/frontend/my-app/src/pages/UploadTorrent.jsx
+++ b/frontend/my-app/src/pages/UploadTorrent.jsx
@@ -4,43 +4,44 @@
   TextField, Button, Stack
 } from '@mui/material';
 import CloudUploadIcon from '@mui/icons-material/CloudUpload';
-// Assuming your base.css is in a relative path like src/styles/base/base.css
-// If your component is in src/components/Upload.js, this path would be ../styles/base/base.css
-// Adjust if your project structure is different.
-// For this example, I'll assume it's correctly linked.
-// import '../styles/base/base.css'; 
+
 
 function Upload() {
   const [title, setTitle] = useState('');
   const [file, setFile] = useState(null);
 
   const handleUpload = () => {
-    const formData = new FormData();
-    if (file) {
-      formData.append('file', file);
-    }
-    formData.append('title', title);
+  const formData = new FormData();
+  if (file) {
+    formData.append('file', file);
+  }
+  formData.append('title', title);
 
-    // Replace with your actual API endpoint and error handling
-    fetch('/api/torrents/upload', {
-      method: 'POST',
-      body: formData
-    })
+  const token = localStorage.getItem('jwt_token'); // 假设你登录后将 token 存在 localStorage
+
+  fetch('http://localhost:8080/api/torrents/upload', {
+    method: 'POST',
+    headers: {
+      Authorization: `Bearer ${token}`,
+    },
+    body: formData,
+  })
     .then(response => {
       if (response.ok) {
-        // Use a more modern way to show messages, e.g., a Snackbar
-        console.log('上传成功'); 
-        alert('上传成功 (建议使用Snackbar等UI组件替代alert)');
+        console.log('上传成功');
+        alert('上传成功');
       } else {
-        console.error('上传失败');
-        alert('上传失败 (建议使用Snackbar等UI组件替代alert)');
+        return response.text().then(text => {
+          console.error('上传失败:', text);
+          alert('上传失败: ' + text);
+        });
       }
     })
     .catch(error => {
       console.error('上传出错:', error);
-      alert('上传出错 (建议使用Snackbar等UI组件替代alert)');
+      alert('上传出错: ' + error.message);
     });
-  };
+};
 
   return (
     <Box sx={{ minHeight: '100vh', py: 4, background: 'linear-gradient(135deg, #2c3e50, #4ca1af)', color: 'white' }}> {/* Moved body styles here for self-containment */}
diff --git a/frontend/my-app/src/services/torrentService.js b/frontend/my-app/src/services/torrentService.js
deleted file mode 100644
index a50f8ac..0000000
--- a/frontend/my-app/src/services/torrentService.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// ./services/torrentService.js
-import axios from 'axios';
-
-// IMPORTANT: Replace with your actual backend API URL
-const API_BASE_URL = 'http://localhost:5000/api';
-
-export const uploadTorrent = async (formData) => {
-    try {
-        const response = await axios.post(`${API_BASE_URL}/torrents/upload`, formData, {
-            headers: {
-                'Content-Type': 'multipart/form-data', // Essential for file uploads
-                // Add any authentication tokens here if your backend requires them
-                // 'Authorization': `Bearer ${localStorage.getItem('authToken')}`
-            },
-            onUploadProgress: (progressEvent) => {
-                // You can use this to show upload progress to the user
-                const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
-                console.log(`Upload progress: ${percentCompleted}%`);
-                // You could pass this percentage back to the component via a callback
-            },
-        });
-        return response.data; // Your backend should return the parsed torrent info here
-    } catch (error) {
-        console.error('Error in uploadTorrent service:', error.response ? error.response.data : error.message);
-        // Throw a more specific error message based on backend response if available
-        throw new Error(error.response?.data?.message || '文件上传失败,请稍后再试。');
-    }
-};
\ No newline at end of file