PHP版本控制系统:高效文档管理

发布于:2025-08-12 ⋅ 阅读:(12) ⋅ 点赞:(0)

系统功能说明

这个PHP版本控制系统实现了以下核心功能:

1. 用户认证与权限管理

  • 用户注册、登录和登出
  • 三级权限系统:管理员(admin)、编辑者(editor)和查看者(viewer)
  • 基于角色的访问控制(RBAC)

2. 仓库管理

  • 创建新仓库
  • 查看仓库列表和详情
  • 仓库统计信息(提交数、分支数、贡献者)

3. 文档版本控制

  • 上传文档到仓库
  • 查看文档历史版本
  • 检索特定版本的文档内容
  • 类似Git的提交机制

4. 分支管理

  • 创建新分支
  • 查看分支列表
  • 合并分支到主分支

5. 权限控制

  • 仓库级别的权限控制
  • 基于用户角色的操作限制

安装与使用

  1. 创建数据库并运行install.php初始化数据库结构
  2. 配置config.php中的数据库连接信息
  3. 将所有文件上传到Web服务器
  4. 访问系统首页进行注册和登录
  5. 管理员可以创建仓库,用户可以上传文档并管理版本

这个系统提供了完整的文档版本控制功能,适合团队协作管理文档,支持权限分级和版本历史追踪。

以下是一些视图文件的示例,用于展示用户界面:

	/* css/style.css */
	* {
	    margin: 0;
	    padding: 0;
	    box-sizing: border-box;
	}
	body {
	    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
	    line-height: 1.6;
	    color: #333;
	    background-color: #f5f5f5;
	}
	.container {
	    max-width: 1200px;
	    margin: 0 auto;
	    padding: 0 20px;
	}
	header {
	    background-color: #2c3e50;
	    color: white;
	    padding: 1rem 0;
	    margin-bottom: 2rem;
	}
	header h1 {
	    display: inline-block;
	    margin-right: 2rem;
	}
	header nav {
	    display: inline-block;
	}
	header nav a {
	    color: white;
	    text-decoration: none;
	    margin-right: 1rem;
	    padding: 0.5rem;
	    border-radius: 4px;
	    transition: background-color 0.3s;
	}
	header nav a:hover {
	    background-color: #34495e;
	}
	main {
	    background-color: white;
	    border-radius: 8px;
	    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
	    padding: 2rem;
	    margin-bottom: 2rem;
	}
	footer {
	    text-align: center;
	    padding: 1rem 0;
	    color: #7f8c8d;
	    font-size: 0.9rem;
	}
	/* Home Page */
	.hero {
	    text-align: center;
	    padding: 3rem 0;
	    background-color: #3498db;
	    color: white;
	    border-radius: 8px;
	    margin-bottom: 2rem;
	}
	.hero h2 {
	    font-size: 2.5rem;
	    margin-bottom: 1rem;
	}
	.hero p {
	    font-size: 1.2rem;
	    margin-bottom: 2rem;
	    max-width: 800px;
	    margin-left: auto;
	    margin-right: auto;
	}
	.cta a {
	    display: inline-block;
	    margin: 0 0.5rem;
	    padding: 0.75rem 1.5rem;
	    border-radius: 4px;
	    text-decoration: none;
	    font-weight: bold;
	}
	.btn {
	    background-color: #2ecc71;
	    color: white;
	}
	.btn-secondary {
	    background-color: #95a5a6;
	    color: white;
	}
	.features {
	    display: flex;
	    justify-content: space-between;
	    margin-top: 2rem;
	}
	.feature {
	    flex: 1;
	    padding: 1.5rem;
	    margin: 0 0.5rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
	}
	.feature h3 {
	    margin-bottom: 1rem;
	    color: #2c3e50;
	}
	/* Dashboard */
	.dashboard-header {
	    margin-bottom: 2rem;
	    padding-bottom: 1rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.dashboard-header h2 {
	    margin-bottom: 0.5rem;
	}
	.repositories h3 {
	    margin-bottom: 1rem;
	}
	.repo-list {
	    display: grid;
	    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
	    gap: 1.5rem;
	}
	.repo-card {
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    padding: 1.5rem;
	    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
	    transition: transform 0.3s, box-shadow 0.3s;
	}
	.repo-card:hover {
	    transform: translateY(-5px);
	    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
	}
	.repo-card h4 {
	    margin-bottom: 0.5rem;
	}
	.repo-card h4 a {
	    color: #3498db;
	    text-decoration: none;
	}
	.repo-card h4 a:hover {
	    text-decoration: underline;
	}
	.repo-meta {
	    margin-top: 1rem;
	    font-size: 0.9rem;
	    color: #7f8c8d;
	}
	.repo-meta span {
	    margin-right: 1rem;
	}
	/* Repository Page */
	.repo-header {
	    margin-bottom: 2rem;
	    padding-bottom: 1rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.repo-header h2 {
	    margin-bottom: 0.5rem;
	}
	.repo-actions {
	    margin-top: 1rem;
	}
	.repo-actions a {
	    margin-right: 0.5rem;
	}
	.repo-stats {
	    display: flex;
	    margin-bottom: 2rem;
	}
	.stat {
	    flex: 1;
	    padding: 1rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	    text-align: center;
	    margin: 0 0.5rem;
	}
	.stat h4 {
	    margin-bottom: 0.5rem;
	    color: #7f8c8d;
	}
	.stat p {
	    font-size: 1.5rem;
	    font-weight: bold;
	    color: #2c3e50;
	}
	.repo-latest-commit, .repo-branches, .repo-documents {
	    margin-bottom: 2rem;
	}
	.repo-latest-commit h3, .repo-branches h3, .repo-documents h3 {
	    margin-bottom: 1rem;
	    padding-bottom: 0.5rem;
	    border-bottom: 1px solid #ecf0f1;
	}
	.commit {
	    padding: 1rem;
	    background-color: #f8f9fa;
	    border-radius: 8px;
	}
	.commit-message {
	    margin-bottom: 0.5rem;
	    font-weight: bold;
	}
	.commit-meta {
	    font-size: 0.9rem;
	    color: #7f8c8d;
	}
	.commit-meta span {
	    margin-right: 1rem;
	}
	.repo-branches ul {
	    list-style-type: none;
	}
	.repo-branches li {
	    padding: 0.5rem 0;
	    border-bottom: 1px solid #ecf0f1;
	}
	.document-list {
	    width: 100%;
	    border-collapse: collapse;
	}
	.document-list th, .document-list td {
	    padding: 0.75rem;
	    text-align: left;
	    border-bottom: 1px solid #ecf0f1;
	}
	.document-list th {
	    background-color: #f8f9fa;
	    font-weight: bold;
	}
	.document-list tr:hover {
	    background-color: #f8f9fa;
	}
	/* Forms */
	.form-container {
	    max-width: 600px;
	    margin: 0 auto;
	}
	.form-container h2 {
	    margin-bottom: 1.5rem;
	    text-align: center;
	}
	.form-group {
	    margin-bottom: 1.5rem;
	}
	.form-group label {
	    display: block;
	    margin-bottom: 0.5rem;
	    font-weight: bold;
	}
	.form-group input[type="text"],
	.form-group input[type="email"],
	.form-group input[type="password"],
	.form-group input[type="file"],
	.form-group textarea {
	    width: 100%;
	    padding: 0.75rem;
	    border: 1px solid #ddd;
	    border-radius: 4px;
	    font-size: 1rem;
	}
	.form-group textarea {
	    resize: vertical;
	}
	.form-group small {
	    display: block;
	    margin-top: 0.25rem;
	    color: #7f8c8d;
	    font-size: 0.9rem;
	}
	.form-actions {
	    text-align: center;
	    margin-top: 2rem;
	}
	.form-actions button {
	    margin: 0 0.5rem;
	}
	.error {
	    padding: 1rem;
	    margin-bottom: 1rem;
	    background-color: #f8d7da;
	    color: #721c24;
	    border-radius: 4px;
	    border: 1px solid #f5c6cb;
	}
	/* Responsive Design */
	@media (max-width: 768px) {
	    header {
	        text-align: center;
	    }
	    header h1 {
	        display: block;
	        margin-bottom: 1rem;
	    }
	    .features {
	        flex-direction: column;
	    }
	    .feature {
	        margin: 0.5rem 0;
	    }
	    .repo-stats {
	        flex-direction: column;
	    }
	    .stat {
	        margin: 0.5rem 0;
	    }
	    .repo-list {
	        grid-template-columns: 1fr;
	    }
	}

view

	<!-- views/upload.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Upload Document - <?php echo htmlspecialchars($repository['name']); ?></title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <a href="index.php?action=repo&id=<?php echo $repository['id']; ?>">Repository</a>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="form-container">
	                <h2>Upload Document</h2>
	                <?php if (isset($error)): ?>
	                    <div class="error"><?php echo htmlspecialchars($error); ?></div>
	                <?php endif; ?>
	                <form action="index.php?action=upload&id=<?php echo $repository['id']; ?>" method="post" enctype="multipart/form-data">
	                    <div class="form-group">
	                        <label for="file">Select Document</label>
	                        <input type="file" id="file" name="file" required>
	                        <small>Allowed file types: <?php echo implode(', ', Config::ALLOWED_EXTENSIONS); ?></small>
	                    </div>
	                    <div class="form-group">
	                        <label for="message">Commit Message</label>
	                        <textarea id="message" name="message" rows="3"></textarea>
	                    </div>
	                    <div class="form-actions">
	                        <button type="submit" class="btn">Upload Document</button>
	                        <a href="index.php?action=repo&id=<?php echo $repository['id']; ?>" class="btn btn-secondary">Cancel</a>
	                    </div>
	                </form>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
	<!-- views/repo.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title><?php echo htmlspecialchars($repository['name']); ?> - Document VCS</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="repo-header">
	                <h2><?php echo htmlspecialchars($repository['name']); ?></h2>
	                <p><?php echo htmlspecialchars($repository['description']); ?></p>
	                <div class="repo-actions">
	                    <?php if ($auth->hasPermission('write', $repository['id'])): ?>
	                        <a href="index.php?action=upload&id=<?php echo $repository['id']; ?>" class="btn">Upload Document</a>
	                        <a href="index.php?action=create_branch&repo_id=<?php echo $repository['id']; ?>" class="btn">Create Branch</a>
	                        <a href="index.php?action=merge_branch&repo_id=<?php echo $repository['id']; ?>" class="btn">Merge Branch</a>
	                    <?php endif; ?>
	                </div>
	            </div>
	            <div class="repo-stats">
	                <div class="stat">
	                    <h4>Commits</h4>
	                    <p><?php echo $stats['commit_count']; ?></p>
	                </div>
	                <div class="stat">
	                    <h4>Branches</h4>
	                    <p><?php echo $stats['branch_count']; ?></p>
	                </div>
	                <div class="stat">
	                    <h4>Contributors</h4>
	                    <p><?php echo count($stats['contributors']); ?></p>
	                </div>
	            </div>
	            <div class="repo-latest-commit">
	                <h3>Latest Commit</h3>
	                <?php if (isset($repository['latest_commit'])): ?>
	                    <div class="commit">
	                        <p class="commit-message"><?php echo htmlspecialchars($repository['latest_commit']['message']); ?></p>
	                        <div class="commit-meta">
	                            <span>Hash: <?php echo substr($repository['latest_commit']['hash'], 0, 7); ?></span>
	                            <span>Author: <?php echo htmlspecialchars($repository['latest_commit']['author']); ?></span>
	                            <span>Date: <?php echo $repository['latest_commit']['date']; ?></span>
	                        </div>
	                    </div>
	                <?php else: ?>
	                    <p>No commits yet</p>
	                <?php endif; ?>
	            </div>
	            <div class="repo-branches">
	                <h3>Branches</h3>
	                <ul>
	                    <?php foreach ($repository['branches'] as $branch): ?>
	                        <li><?php echo htmlspecialchars($branch); ?></li>
	                    <?php endforeach; ?>
	                </ul>
	            </div>
	            <div class="repo-documents">
	                <h3>Documents</h3>
	                <?php if (empty($documents)): ?>
	                    <p>No documents found.</p>
	                <?php else: ?>
	                    <table class="document-list">
	                        <thead>
	                            <tr>
	                                <th>Name</th>
	                                <th>Actions</th>
	                            </tr>
	                        </thead>
	                        <tbody>
	                            <?php foreach ($documents as $doc): ?>
	                                <tr>
	                                    <td><?php echo htmlspecialchars($doc['name']); ?></td>
	                                    <td>
	                                        <a href="index.php?action=history&repo_id=<?php echo $repository['id']; ?>&file=<?php echo urlencode($doc['name']); ?>">View History</a>
	                                    </td>
	                                </tr>
	                            <?php endforeach; ?>
	                        </tbody>
	                    </table>
	                <?php endif; ?>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
	<!-- views/dashboard.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Dashboard - Document VCS</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <a href="index.php?action=home">Dashboard</a>
	                <?php if ($auth->getUser()['role'] === Config::ROLE_ADMIN): ?>
	                    <a href="index.php?action=create_repo">Create Repository</a>
	                <?php endif; ?>
	                <a href="index.php?action=logout">Logout</a>
	            </nav>
	        </header>
	        <main>
	            <div class="dashboard-header">
	                <h2>Welcome, <?php echo htmlspecialchars($auth->getUser()['username']); ?></h2>
	                <p>Role: <?php echo ucfirst($auth->getUser()['role']); ?></p>
	            </div>
	            <div class="repositories">
	                <h3>Your Repositories</h3>
	                <?php if (empty($repositories)): ?>
	                    <p>No repositories found. <?php if ($auth->getUser()['role'] === Config::ROLE_ADMIN): ?>
	                        <a href="index.php?action=create_repo">Create one</a>
	                    <?php endif; ?></p>
	                <?php else: ?>
	                    <div class="repo-list">
	                        <?php foreach ($repositories as $repo): ?>
	                            <div class="repo-card">
	                                <h4><a href="index.php?action=repo&id=<?php echo $repo['id']; ?>"><?php echo htmlspecialchars($repo['name']); ?></a></h4>
	                                <p><?php echo htmlspecialchars($repo['description']); ?></p>
	                                <div class="repo-meta">
	                                    <span>Created: <?php echo date('M j, Y', strtotime($repo['created_at'])); ?></span>
	                                    <span>Owner: <?php echo htmlspecialchars($repo['owner_id']); ?></span>
	                                </div>
	                            </div>
	                        <?php endforeach; ?>
	                    </div>
	                <?php endif; ?>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
	<!-- views/home.php -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
	    <title>Document Version Control System</title>
	    <link rel="stylesheet" href="css/style.css">
	</head>
	<body>
	    <div class="container">
	        <header>
	            <h1>Document Version Control System</h1>
	            <nav>
	                <a href="index.php">Home</a>
	                <?php if ($auth->isLoggedIn()): ?>
	                    <a href="index.php?action=logout">Logout</a>
	                <?php else: ?>
	                    <a href="index.php?action=login">Login</a>
	                    <a href="index.php?action=register">Register</a>
	                <?php endif; ?>
	            </nav>
	        </header>
	        <main>
	            <div class="hero">
	                <h2>Welcome to Document VCS</h2>
	                <p>A powerful version control system for managing your documents with Git-like features.</p>
	                <?php if (!$auth->isLoggedIn()): ?>
	                    <div class="cta">
	                        <a href="index.php?action=login" class="btn">Login</a>
	                        <a href="index.php?action=register" class="btn btn-secondary">Register</a>
	                    </div>
	                <?php else: ?>
	                    <div class="cta">
	                        <a href="index.php?action=home" class="btn">Go to Dashboard</a>
	                    </div>
	                <?php endif; ?>
	            </div>
	            <div class="features">
	                <div class="feature">
	                    <h3>Version Control</h3>
	                    <p>Track changes, view history, and revert to previous versions of your documents.</p>
	                </div>
	                <div class="feature">
	                    <h3>Branching & Merging</h3>
	                    <p>Create branches for experimental changes and merge them back when ready.</p>
	                </div>
	                <div class="feature">
	                    <h3>Permission Management</h3>
	                    <p>Control who can view, edit, or administer your repositories with fine-grained permissions.</p>
	                </div>
	            </div>
	        </main>
	        <footer>
	            <p>&copy; <?php echo date('Y'); ?> Document Version Control System</p>
	        </footer>
	    </div>
	</body>
	</html>
	<?php
	// config.php - 配置文件
	class Config {
	    // 数据库配置
	    const DB_HOST = 'localhost';
	    const DB_NAME = 'document_vcs';
	    const DB_USER = 'root';
	    const DB_PASS = '';
	    // 文件存储路径
	    const REPO_PATH = __DIR__ . '/repositories/';
	    const UPLOAD_PATH = __DIR__ . '/uploads/';
	    // 权限级别
	    const ROLE_ADMIN = 'admin';      // 管理员:完全控制
	    const ROLE_EDITOR = 'editor';    // 编辑者:可编辑、提交
	    const ROLE_VIEWER = 'viewer';    // 查看者:只读权限
	    // 系统设置
	    const MAX_FILE_SIZE = 10485760;  // 10MB
	    const ALLOWED_EXTENSIONS = ['doc', 'docx', 'txt', 'pdf', 'md'];
	}
	// db.php - 数据库连接与操作
	class Database {
	    private static $instance = null;
	    private $connection;
	    private function __construct() {
	        try {
	            $this->connection = new PDO(
	                "mysql:host=" . Config::DB_HOST . ";dbname=" . Config::DB_NAME,
	                Config::DB_USER,
	                Config::DB_PASS
	            );
	            $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	        } catch (PDOException $e) {
	            die("Database connection failed: " . $e->getMessage());
	        }
	    }
	    public static function getInstance() {
	        if (!self::$instance) {
	            self::$instance = new self();
	        }
	        return self::$instance;
	    }
	    public function getConnection() {
	        return $this->connection;
	    }
	    public function query($sql, $params = []) {
	        try {
	            $stmt = $this->connection->prepare($sql);
	            $stmt->execute($params);
	            return $stmt;
	        } catch (PDOException $e) {
	            die("Query failed: " . $e->getMessage());
	        }
	    }
	    public function insert($table, $data) {
	        $columns = implode(', ', array_keys($data));
	        $placeholders = implode(', ', array_fill(0, count($data), '?'));
	        $sql = "INSERT INTO $table ($columns) VALUES ($placeholders)";
	        $this->query($sql, array_values($data));
	        return $this->connection->lastInsertId();
	    }
	    public function update($table, $data, $where, $whereParams = []) {
	        $setParts = [];
	        foreach ($data as $key => $value) {
	            $setParts[] = "$key = ?";
	        }
	        $setClause = implode(', ', $setParts);
	        $sql = "UPDATE $table SET $setClause WHERE $where";
	        $params = array_merge(array_values($data), $whereParams);
	        $this->query($sql, $params);
	        return $this->connection->rowCount();
	    }
	    public function delete($table, $where, $params = []) {
	        $sql = "DELETE FROM $table WHERE $where";
	        $this->query($sql, $params);
	        return $this->connection->rowCount();
	    }
	    public function select($table, $where = '', $params = [], $columns = '*') {
	        $sql = "SELECT $columns FROM $table";
	        if ($where) {
	            $sql .= " WHERE $where";
	        }
	        return $this->query($sql, $params)->fetchAll(PDO::FETCH_ASSOC);
	    }
	    public function selectOne($table, $where = '', $params = [], $columns = '*') {
	        $sql = "SELECT $columns FROM $table";
	        if ($where) {
	            $sql .= " WHERE $where";
	        }
	        $result = $this->query($sql, $params)->fetch(PDO::FETCH_ASSOC);
	        return $result ?: null;
	    }
	}
	// auth.php - 用户认证与权限管理
	class Auth {
	    private $db;
	    private $user = null;
	    public function __construct() {
	        $this->db = Database::getInstance();
	        session_start();
	        if (isset($_SESSION['user_id'])) {
	            $this->user = $this->db->selectOne(
	                'users', 
	                'id = ?', 
	                [$_SESSION['user_id']]
	            );
	        }
	    }
	    public function login($username, $password) {
	        $user = $this->db->selectOne(
	            'users', 
	            'username = ?', 
	            [$username]
	        );
	        if ($user && password_verify($password, $user['password'])) {
	            $_SESSION['user_id'] = $user['id'];
	            $this->user = $user;
	            return true;
	        }
	        return false;
	    }
	    public function logout() {
	        session_destroy();
	        $this->user = null;
	    }
	    public function register($username, $email, $password, $role = Config::ROLE_VIEWER) {
	        // 检查用户名是否已存在
	        if ($this->db->selectOne('users', 'username = ?', [$username])) {
	            throw new Exception("Username already exists");
	        }
	        // 检查邮箱是否已存在
	        if ($this->db->selectOne('users', 'email = ?', [$email])) {
	            throw new Exception("Email already exists");
	        }
	        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
	        return $this->db->insert('users', [
	            'username' => $username,
	            'email' => $email,
	            'password' => $hashedPassword,
	            'role' => $role,
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	    }
	    public function isLoggedIn() {
	        return $this->user !== null;
	    }
	    public function getUser() {
	        return $this->user;
	    }
	    public function hasPermission($permission, $documentId = null) {
	        if (!$this->isLoggedIn()) {
	            return false;
	        }
	        // 管理员拥有所有权限
	        if ($this->user['role'] === Config::ROLE_ADMIN) {
	            return true;
	        }
	        // 检查文档特定权限
	        if ($documentId) {
	            $docPermission = $this->db->selectOne(
	                'document_permissions',
	                'document_id = ? AND user_id = ?',
	                [$documentId, $this->user['id']]
	            );
	            if ($docPermission) {
	                return $docPermission['permission'] === $permission;
	            }
	        }
	        // 默认权限检查
	        switch ($permission) {
	            case 'read':
	                return in_array($this->user['role'], [Config::ROLE_EDITOR, Config::ROLE_VIEWER]);
	            case 'write':
	                return $this->user['role'] === Config::ROLE_EDITOR;
	            case 'admin':
	                return false;
	            default:
	                return false;
	        }
	    }
	    public function requireLogin() {
	        if (!$this->isLoggedIn()) {
	            header('Location: login.php');
	            exit;
	        }
	    }
	    public function requirePermission($permission, $documentId = null) {
	        $this->requireLogin();
	        if (!$this->hasPermission($permission, $documentId)) {
	            http_response_code(403);
	            die("You don't have permission to perform this action");
	        }
	    }
	}
	// repository.php - 仓库管理与版本控制
	class Repository {
	    private $db;
	    private $auth;
	    private $repoPath;
	    public function __construct($auth) {
	        $this->db = Database::getInstance();
	        $this->auth = $auth;
	        $this->repoPath = Config::REPO_PATH;
	        // 确保仓库目录存在
	        if (!file_exists($this->repoPath)) {
	            mkdir($this->repoPath, 0755, true);
	        }
	    }
	    public function createRepository($name, $description = '') {
	        $this->auth->requirePermission('admin');
	        // 检查仓库名称是否已存在
	        if ($this->db->selectOne('repositories', 'name = ?', [$name])) {
	            throw new Exception("Repository already exists");
	        }
	        // 创建数据库记录
	        $repoId = $this->db->insert('repositories', [
	            'name' => $name,
	            'description' => $description,
	            'owner_id' => $this->auth->getUser()['id'],
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	        // 创建仓库目录
	        $repoDir = $this->repoPath . $repoId;
	        if (!mkdir($repoDir, 0755, true)) {
	            throw new Exception("Failed to create repository directory");
	        }
	        // 初始化Git仓库
	        $this->executeGitCommand($repoDir, 'init');
	        $this->executeGitCommand($repoDir, 'config user.name "' . $this->auth->getUser()['username'] . '"');
	        $this->executeGitCommand($repoDir, 'config user.email "' . $this->auth->getUser()['email'] . '"');
	        // 创建初始提交
	        file_put_contents($repoDir . '/README.md', "# $name\n\n$description");
	        $this->executeGitCommand($repoDir, 'add README.md');
	        $this->executeGitCommand($repoDir, 'commit -m "Initial commit"');
	        return $repoId;
	    }
	    public function getRepositories() {
	        $this->auth->requireLogin();
	        $userId = $this->auth->getUser()['id'];
	        // 管理员可以查看所有仓库
	        if ($this->auth->getUser()['role'] === Config::ROLE_ADMIN) {
	            return $this->db->select('repositories');
	        }
	        // 其他用户只能查看有权限的仓库
	        return $this->db->select(
	            'repositories r',
	            'r.owner_id = ? OR EXISTS (
	                SELECT 1 FROM repository_permissions rp 
	                WHERE rp.repository_id = r.id AND rp.user_id = ?
	            )',
	            [$userId, $userId]
	        );
	    }
	    public function getRepository($id) {
	        $this->auth->requirePermission('read', $id);
	        $repo = $this->db->selectOne('repositories', 'id = ?', [$id]);
	        if (!$repo) {
	            throw new Exception("Repository not found");
	        }
	        $repoDir = $this->repoPath . $id;
	        // 获取最新提交信息
	        $latestCommit = $this->executeGitCommand($repoDir, 'log -1 --pretty=format:"%H|%s|%an|%ad" --date=short');
	        if ($latestCommit) {
	            list($hash, $message, $author, $date) = explode('|', $latestCommit);
	            $repo['latest_commit'] = [
	                'hash' => $hash,
	                'message' => $message,
	                'author' => $author,
	                'date' => $date
	            ];
	        }
	        // 获取分支列表
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        $branches = [];
	        if ($branchesOutput) {
	            $lines = explode("\n", trim($branchesOutput));
	            foreach ($lines as $line) {
	                $branch = trim(str_replace('*', '', $line));
	                $branches[] = $branch;
	            }
	        }
	        $repo['branches'] = $branches;
	        return $repo;
	    }
	    public function addDocument($repoId, $file, $message = '') {
	        $this->auth->requirePermission('write', $repoId);
	        // 验证文件
	        if ($file['error'] !== UPLOAD_ERR_OK) {
	            throw new Exception("File upload error");
	        }
	        if ($file['size'] > Config::MAX_FILE_SIZE) {
	            throw new Exception("File too large");
	        }
	        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
	        if (!in_array($extension, Config::ALLOWED_EXTENSIONS)) {
	            throw new Exception("File type not allowed");
	        }
	        $repoDir = $this->repoPath . $repoId;
	        $fileName = $file['name'];
	        $filePath = $repoDir . '/' . $fileName;
	        // 移动文件到仓库目录
	        if (!move_uploaded_file($file['tmp_name'], $filePath)) {
	            throw new Exception("Failed to save file");
	        }
	        // 添加到Git并提交
	        $this->executeGitCommand($repoDir, 'add "' . $fileName . '"');
	        $commitMessage = $message ?: "Add document: $fileName";
	        $this->executeGitCommand($repoDir, 'commit -m "' . $commitMessage . '"');
	        // 记录到数据库
	        $commitHash = trim($this->executeGitCommand($repoDir, 'rev-parse HEAD'));
	        $this->db->insert('documents', [
	            'repository_id' => $repoId,
	            'name' => $fileName,
	            'file_path' => $filePath,
	            'commit_hash' => $commitHash,
	            'uploaded_by' => $this->auth->getUser()['id'],
	            'uploaded_at' => date('Y-m-d H:i:s')
	        ]);
	        return $commitHash;
	    }
	    public function getDocuments($repoId) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取Git中的文件列表
	        $filesOutput = $this->executeGitCommand($repoDir, 'ls-files');
	        $files = [];
	        if ($filesOutput) {
	            $lines = explode("\n", trim($filesOutput));
	            foreach ($lines as $line) {
	                $files[] = [
	                    'name' => $line,
	                    'path' => $repoDir . '/' . $line
	                ];
	            }
	        }
	        return $files;
	    }
	    public function getDocumentHistory($repoId, $fileName) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取文件历史记录
	        $historyOutput = $this->executeGitCommand(
	            $repoDir, 
	            'log --pretty=format:"%H|%s|%an|%ad" --date=short -- "' . $fileName . '"'
	        );
	        $history = [];
	        if ($historyOutput) {
	            $lines = explode("\n", trim($historyOutput));
	            foreach ($lines as $line) {
	                list($hash, $message, $author, $date) = explode('|', $line);
	                $history[] = [
	                    'hash' => $hash,
	                    'message' => $message,
	                    'author' => $author,
	                    'date' => $date
	                ];
	            }
	        }
	        return $history;
	    }
	    public function getDocumentVersion($repoId, $fileName, $commitHash) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取特定版本的文件内容
	        $content = $this->executeGitCommand($repoDir, 'show ' . $commitHash . ':"' . $fileName . '"');
	        return $content;
	    }
	    public function createBranch($repoId, $branchName, $sourceBranch = 'main') {
	        $this->auth->requirePermission('write', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 检查分支是否已存在
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        if ($branchesOutput && strpos($branchesOutput, $branchName) !== false) {
	            throw new Exception("Branch already exists");
	        }
	        // 创建新分支
	        $this->executeGitCommand($repoDir, 'branch ' . $branchName . ' ' . $sourceBranch);
	        // 记录到数据库
	        $this->db->insert('branches', [
	            'repository_id' => $repoId,
	            'name' => $branchName,
	            'source_branch' => $sourceBranch,
	            'created_by' => $this->auth->getUser()['id'],
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	        return true;
	    }
	    public function mergeBranch($repoId, $sourceBranch, $targetBranch = 'main') {
	        $this->auth->requirePermission('write', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 切换到目标分支
	        $this->executeGitCommand($repoDir, 'checkout ' . $targetBranch);
	        // 合并源分支
	        $mergeOutput = $this->executeGitCommand($repoDir, 'merge ' . $sourceBranch . ' --no-edit');
	        // 记录合并操作
	        $this->db->insert('merges', [
	            'repository_id' => $repoId,
	            'source_branch' => $sourceBranch,
	            'target_branch' => $targetBranch,
	            'merged_by' => $this->auth->getUser()['id'],
	            'merged_at' => date('Y-m-d H:i:s'),
	            'status' => 'success'
	        ]);
	        return $mergeOutput;
	    }
	    public function getRepositoryStats($repoId) {
	        $this->auth->requirePermission('read', $repoId);
	        $repoDir = $this->repoPath . $repoId;
	        // 获取提交总数
	        $commitCount = trim($this->executeGitCommand($repoDir, 'rev-list --count HEAD'));
	        // 获取分支数量
	        $branchesOutput = $this->executeGitCommand($repoDir, 'branch -l');
	        $branchCount = $branchesOutput ? count(explode("\n", trim($branchesOutput))) : 0;
	        // 获取贡献者数量
	        $contributorsOutput = $this->executeGitCommand($repoDir, 'shortlog -s | cut -c8-');
	        $contributors = [];
	        if ($contributorsOutput) {
	            $lines = explode("\n", trim($contributorsOutput));
	            foreach ($lines as $line) {
	                $contributors[] = trim($line);
	            }
	        }
	        return [
	            'commit_count' => (int)$commitCount,
	            'branch_count' => $branchCount,
	            'contributors' => $contributors
	        ];
	    }
	    private function executeGitCommand($repoDir, $command) {
	        $output = [];
	        $returnValue = 0;
	        $fullCommand = "cd $repoDir && git $command 2>&1";
	        exec($fullCommand, $output, $returnValue);
	        if ($returnValue !== 0) {
	            throw new Exception("Git command failed: " . implode("\n", $output));
	        }
	        return implode("\n", $output);
	    }
	}
	// install.php - 数据库安装脚本
	function installDatabase() {
	    $db = Database::getInstance();
	    // 创建用户表
	    $db->query("CREATE TABLE IF NOT EXISTS users (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        username VARCHAR(50) UNIQUE NOT NULL,
	        email VARCHAR(100) UNIQUE NOT NULL,
	        password VARCHAR(255) NOT NULL,
	        role ENUM('admin', 'editor', 'viewer') NOT NULL DEFAULT 'viewer',
	        created_at DATETIME NOT NULL
	    )");
	    // 创建仓库表
	    $db->query("CREATE TABLE IF NOT EXISTS repositories (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        name VARCHAR(100) UNIQUE NOT NULL,
	        description TEXT,
	        owner_id INT NOT NULL,
	        created_at DATETIME NOT NULL,
	        FOREIGN KEY (owner_id) REFERENCES users(id)
	    )");
	    // 创建文档表
	    $db->query("CREATE TABLE IF NOT EXISTS documents (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        name VARCHAR(255) NOT NULL,
	        file_path VARCHAR(255) NOT NULL,
	        commit_hash VARCHAR(40) NOT NULL,
	        uploaded_by INT NOT NULL,
	        uploaded_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (uploaded_by) REFERENCES users(id)
	    )");
	    // 创建分支表
	    $db->query("CREATE TABLE IF NOT EXISTS branches (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        name VARCHAR(100) NOT NULL,
	        source_branch VARCHAR(100) NOT NULL,
	        created_by INT NOT NULL,
	        created_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (created_by) REFERENCES users(id),
	        UNIQUE KEY (repository_id, name)
	    )");
	    // 创建合并记录表
	    $db->query("CREATE TABLE IF NOT EXISTS merges (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        source_branch VARCHAR(100) NOT NULL,
	        target_branch VARCHAR(100) NOT NULL,
	        merged_by INT NOT NULL,
	        merged_at DATETIME NOT NULL,
	        status ENUM('success', 'failed') NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (merged_by) REFERENCES users(id)
	    )");
	    // 创建仓库权限表
	    $db->query("CREATE TABLE IF NOT EXISTS repository_permissions (
	        id INT AUTO_INCREMENT PRIMARY KEY,
	        repository_id INT NOT NULL,
	        user_id INT NOT NULL,
	        permission ENUM('read', 'write', 'admin') NOT NULL,
	        granted_at DATETIME NOT NULL,
	        FOREIGN KEY (repository_id) REFERENCES repositories(id),
	        FOREIGN KEY (user_id) REFERENCES users(id),
	        UNIQUE KEY (repository_id, user_id)
	    )");
	    // 创建默认管理员账户
	    $adminExists = $db->selectOne('users', 'role = ?', [Config::ROLE_ADMIN]);
	    if (!$adminExists) {
	        $db->insert('users', [
	            'username' => 'admin',
	            'email' => 'admin@example.com',
	            'password' => password_hash('admin123', PASSWORD_DEFAULT),
	            'role' => Config::ROLE_ADMIN,
	            'created_at' => date('Y-m-d H:i:s')
	        ]);
	    }
	    return "Database installed successfully!";
	}
	// index.php - 主入口文件
	require_once 'config.php';
	require_once 'db.php';
	require_once 'auth.php';
	require_once 'repository.php';
	// 初始化
	$auth = new Auth();
	$repo = new Repository($auth);
	// 处理请求
	$action = $_GET['action'] ?? 'home';
	try {
	    switch ($action) {
	        case 'home':
	            // 首页
	            if ($auth->isLoggedIn()) {
	                $repositories = $repo->getRepositories();
	                include 'views/dashboard.php';
	            } else {
	                include 'views/home.php';
	            }
	            break;
	        case 'login':
	            // 登录
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $username = $_POST['username'] ?? '';
	                $password = $_POST['password'] ?? '';
	                if ($auth->login($username, $password)) {
	                    header('Location: index.php');
	                    exit;
	                } else {
	                    $error = "Invalid username or password";
	                    include 'views/login.php';
	                }
	            } else {
	                include 'views/login.php';
	            }
	            break;
	        case 'logout':
	            // 登出
	            $auth->logout();
	            header('Location: index.php');
	            break;
	        case 'register':
	            // 注册
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $username = $_POST['username'] ?? '';
	                $email = $_POST['email'] ?? '';
	                $password = $_POST['password'] ?? '';
	                $confirmPassword = $_POST['confirm_password'] ?? '';
	                if ($password !== $confirmPassword) {
	                    $error = "Passwords do not match";
	                    include 'views/register.php';
	                } else {
	                    try {
	                        $auth->register($username, $email, $password);
	                        $success = "Registration successful. Please login.";
	                        include 'views/login.php';
	                    } catch (Exception $e) {
	                        $error = $e->getMessage();
	                        include 'views/register.php';
	                    }
	                }
	            } else {
	                include 'views/register.php';
	            }
	            break;
	        case 'create_repo':
	            // 创建仓库
	            $auth->requirePermission('admin');
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $name = $_POST['name'] ?? '';
	                $description = $_POST['description'] ?? '';
	                try {
	                    $repoId = $repo->createRepository($name, $description);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    include 'views/create_repo.php';
	                }
	            } else {
	                include 'views/create_repo.php';
	            }
	            break;
	        case 'repo':
	            // 仓库详情
	            $repoId = $_GET['id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            $repository = $repo->getRepository($repoId);
	            $documents = $repo->getDocuments($repoId);
	            $stats = $repo->getRepositoryStats($repoId);
	            include 'views/repo.php';
	            break;
	        case 'upload':
	            // 上传文档
	            $repoId = $_GET['id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $message = $_POST['message'] ?? '';
	                try {
	                    $repo->addDocument($repoId, $_FILES['file'], $message);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/upload.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/upload.php';
	            }
	            break;
	        case 'history':
	            // 文档历史
	            $repoId = $_GET['repo_id'] ?? 0;
	            $fileName = $_GET['file'] ?? '';
	            if (!$repoId || !$fileName) {
	                header('Location: index.php');
	                exit;
	            }
	            $history = $repo->getDocumentHistory($repoId, $fileName);
	            $repository = $repo->getRepository($repoId);
	            include 'views/history.php';
	            break;
	        case 'version':
	            // 文档版本
	            $repoId = $_GET['repo_id'] ?? 0;
	            $fileName = $_GET['file'] ?? '';
	            $commitHash = $_GET['hash'] ?? '';
	            if (!$repoId || !$fileName || !$commitHash) {
	                header('Location: index.php');
	                exit;
	            }
	            $content = $repo->getDocumentVersion($repoId, $fileName, $commitHash);
	            $repository = $repo->getRepository($repoId);
	            include 'views/version.php';
	            break;
	        case 'create_branch':
	            // 创建分支
	            $repoId = $_GET['repo_id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $branchName = $_POST['name'] ?? '';
	                $sourceBranch = $_POST['source'] ?? 'main';
	                try {
	                    $repo->createBranch($repoId, $branchName, $sourceBranch);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/create_branch.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/create_branch.php';
	            }
	            break;
	        case 'merge_branch':
	            // 合并分支
	            $repoId = $_GET['repo_id'] ?? 0;
	            if (!$repoId) {
	                header('Location: index.php');
	                exit;
	            }
	            if ($_SERVER['REQUEST_METHOD'] === 'POST') {
	                $sourceBranch = $_POST['source'] ?? '';
	                $targetBranch = $_POST['target'] ?? 'main';
	                try {
	                    $result = $repo->mergeBranch($repoId, $sourceBranch, $targetBranch);
	                    header("Location: index.php?action=repo&id=$repoId");
	                    exit;
	                } catch (Exception $e) {
	                    $error = $e->getMessage();
	                    $repository = $repo->getRepository($repoId);
	                    include 'views/merge_branch.php';
	                }
	            } else {
	                $repository = $repo->getRepository($repoId);
	                include 'views/merge_branch.php';
	            }
	            break;
	        case 'install':
	            // 安装数据库
	            echo installDatabase();
	            break;
	        default:
	            // 默认首页
	            include 'views/home.php';
	            break;
	    }
	} catch (Exception $e) {
	    $error = $e->getMessage();
	    include 'views/error.php';
	}
	?>