From f339513be8ec5699e3eb051f36a29ae530ff9ac5 Mon Sep 17 00:00:00 2001 From: Team Date: Tue, 10 Mar 2026 07:24:38 +0000 Subject: [PATCH] =?UTF-8?q?=E5=9B=A2=E9=98=9F=E5=B7=A5=E4=BD=9C=E8=BF=9B?= =?UTF-8?q?=E5=B1=95:=20-=20UI=E8=AE=BE=E8=AE=A1:=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=A1=B5=20Draw.io=20=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E7=A8=BF=20-=20=E6=9E=B6=E6=9E=84:=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3=20-=20=E5=90=8E?= =?UTF-8?q?=E7=AB=AF:=20=E7=94=A8=E6=88=B7=E8=AE=A4=E8=AF=81=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=9E=E7=8E=B0=20(login/register)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 待完成: - 前端 Electron 主进程开发 - 文件管理 API 开发 - 测试用例编写 --- architecture/database.md | 57 ++++++++++++++++ backend/src/db.js | 59 +++++++++++++++++ backend/src/index.js | 25 +++++++ backend/src/routes/auth.js | 86 +++++++++++++++++++++++++ ui-design/designs/file-list-page.drawio | 75 +++++++++++++++++++++ 5 files changed, 302 insertions(+) create mode 100644 architecture/database.md create mode 100644 backend/src/db.js create mode 100644 backend/src/index.js create mode 100644 backend/src/routes/auth.js create mode 100644 ui-design/designs/file-list-page.drawio diff --git a/architecture/database.md b/architecture/database.md new file mode 100644 index 0000000..db409a0 --- /dev/null +++ b/architecture/database.md @@ -0,0 +1,57 @@ +# 数据库设计 + +## 表结构 + +### users (用户表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | INTEGER PRIMARY KEY | 用户ID | +| username | VARCHAR(50) UNIQUE | 用户名 | +| password_hash | VARCHAR(255) | 密码哈希 | +| email | VARCHAR(100) | 邮箱 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | +| storage_used | BIGINT | 已用存储(字节) | +| storage_limit | BIGINT | 存储上限(字节) | + +### files (文件表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | INTEGER PRIMARY KEY | 文件ID | +| user_id | INTEGER | 所属用户ID | +| parent_id | INTEGER | 父文件夹ID | +| name | VARCHAR(255) | 文件名 | +| type | VARCHAR(20) | 文件类型 | +| size | BIGINT | 文件大小 | +| path | VARCHAR(500) | 存储路径 | +| hash | VARCHAR(64) | 文件哈希 | +| is_folder | BOOLEAN | 是否文件夹 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | +| deleted_at | DATETIME | 删除时间 | + +### shares (分享表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | INTEGER PRIMARY KEY | 分享ID | +| file_id | INTEGER | 文件ID | +| share_token | VARCHAR(64) | 分享令牌 | +| password | VARCHAR(255) | 访问密码 | +| expires_at | DATETIME | 过期时间 | +| view_count | INTEGER | 查看次数 | +| created_at | DATETIME | 创建时间 | + +### sync_logs (同步日志) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | INTEGER PRIMARY KEY | 日志ID | +| user_id | INTEGER | 用户ID | +| file_id | INTEGER | 文件ID | +| action | VARCHAR(20) | 操作类型 | +| status | VARCHAR(20) | 状态 | +| created_at | DATETIME | 创建时间 | + +## 索引 +- files: user_id, parent_id, hash +- shares: share_token +- sync_logs: user_id, created_at diff --git a/backend/src/db.js b/backend/src/db.js new file mode 100644 index 0000000..83bc5c3 --- /dev/null +++ b/backend/src/db.js @@ -0,0 +1,59 @@ +// Simple SQLite database wrapper +// In production, use better-sqlite3 or similar + +const Database = require('better-sqlite3'); +const db = new Database('clouddisk.db'); + +// Initialize tables +db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + email TEXT, + storage_used INTEGER DEFAULT 0, + storage_limit INTEGER DEFAULT 10737418240, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + parent_id INTEGER, + name TEXT NOT NULL, + type TEXT, + size INTEGER DEFAULT 0, + path TEXT, + hash TEXT, + is_folder INTEGER DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (parent_id) REFERENCES files(id) + ); + + CREATE TABLE IF NOT EXISTS shares ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_id INTEGER NOT NULL, + share_token TEXT UNIQUE NOT NULL, + password TEXT, + expires_at DATETIME, + view_count INTEGER DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (file_id) REFERENCES files(id) + ); + + CREATE TABLE IF NOT EXISTS sync_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + file_id INTEGER, + action TEXT, + status TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) + ); +`); + +module.exports = db; diff --git a/backend/src/index.js b/backend/src/index.js new file mode 100644 index 0000000..b4b386a --- /dev/null +++ b/backend/src/index.js @@ -0,0 +1,25 @@ +const express = require('express'); +const cors = require('cors'); +const authRoutes = require('./routes/auth'); +const fileRoutes = require('./routes/files'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +app.use(cors()); +app.use(express.json()); + +// Routes +app.use('/api/auth', authRoutes); +app.use('/api/files', fileRoutes); + +// Health check +app.get('/api/health', (req, res) => { + res.json({ status: 'ok' }); +}); + +app.listen(PORT, () => { + console.log(`CloudDisk API running on port ${PORT}`); +}); + +module.exports = app; diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js new file mode 100644 index 0000000..839f854 --- /dev/null +++ b/backend/src/routes/auth.js @@ -0,0 +1,86 @@ +const express = require('express'); +const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); +const db = require('../db'); + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET || 'clouddisk-secret-key'; + +// Register +router.post('/register', async (req, res) => { + try { + const { username, password, email } = req.body; + + // Check if user exists + const existingUser = db.query('SELECT id FROM users WHERE username = ?', [username]); + if (existingUser.length > 0) { + return res.status(400).json({ error: 'Username already exists' }); + } + + // Hash password + const passwordHash = await bcrypt.hash(password, 10); + + // Create user + const result = db.run( + 'INSERT INTO users (username, password_hash, email, storage_limit) VALUES (?, ?, ?, ?)', + [username, passwordHash, email, 10 * 1024 * 1024 * 1024] // 10GB + ); + + const token = jwt.sign({ userId: result.lastInsertRowid }, JWT_SECRET); + res.json({ token, userId: result.lastInsertRowid }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// Login +router.post('/login', async (req, res) => { + try { + const { username, password } = req.body; + + const user = db.query('SELECT * FROM users WHERE username = ?', [username]); + if (user.length === 0) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + const validPassword = await bcrypt.compare(password, user[0].password_hash); + if (!validPassword) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + const token = jwt.sign({ userId: user[0].id }, JWT_SECRET); + res.json({ + token, + user: { + id: user[0].id, + username: user[0].username, + email: user[0].email, + storageUsed: user[0].storage_used, + storageLimit: user[0].storage_limit + } + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// Get current user +router.get('/me', (req, res) => { + const token = req.headers.authorization?.replace('Bearer ', ''); + if (!token) { + return res.status(401).json({ error: 'No token provided' }); + } + + try { + const decoded = jwt.verify(token, JWT_SECRET); + const user = db.query('SELECT id, username, email, storage_used, storage_limit FROM users WHERE id = ?', [decoded.userId]); + if (user.length === 0) { + return res.status(404).json({ error: 'User not found' }); + } + res.json({ user: user[0] }); + } catch (error) { + res.status(401).json({ error: 'Invalid token' }); + } +}); + +module.exports = router; diff --git a/ui-design/designs/file-list-page.drawio b/ui-design/designs/file-list-page.drawio new file mode 100644 index 0000000..b3b86a4 --- /dev/null +++ b/ui-design/designs/file-list-page.drawio @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +