吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 623|回复: 7
收起左侧

[学习记录] 本地账号密码存储软件

  [复制链接]
yhx5773489 发表于 2025-11-26 09:32
本帖最后由 yhx5773489 于 2025-12-1 09:07 编辑

每个人都会注册很多网站以及一些重要信息需要留存记录,闲来无事就用AI生成然后微调生成了一份基于.db的形式的账号密码保存功能,同时开放源代码以及.exe文件供大家使用!
代码使用的是Python我使用的版本为3.9

一下为界面的相关截图
运行exe文件启动服务

启动服务

启动服务

界面截图如下

登录界面

登录界面

登录成功后界面

登录成功后界面

具体功能自行探索吧
代码如下:
看板代码登录成功后:
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>密码管理系统</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f8fafc;
            min-height: 100vh;
        }

        .header {
            background: white;
            border-bottom: 1px solid #e5e7eb;
            padding: 16px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .header-title {
            font-size: 20px;
            font-weight: 600;
            color: #1f2937;
        }

        .header-right {
            display: flex;
            align-items: center;
            gap: 16px;
        }

        .welcome-text {
            color: #4f46e5;
            font-size: 14px;
        }

        .logout-btn {
            background: none;
            border: none;
            color: #6b7280;
            font-size: 14px;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .logout-btn:hover {
            color: #374151;
        }

        .nav-tabs {
            background: white;
            border-bottom: 1px solid #e5e7eb;
            padding: 0 24px;
            display: flex;
            gap: 0;
        }

        .nav-tab {
            padding: 12px 24px;
            background: none;
            border: none;
            font-size: 14px;
            cursor: pointer;
            border-radius: 8px 8px 0 0;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .nav-tab.active {
            background: #374151;
            color: white;
        }

        .nav-tab:not(.active) {
            color: #6b7280;
        }

        .nav-tab:not(.active):hover {
            background: #f3f4f6;
            color: #374151;
        }

        .main-content {
            padding: 24px;
            max-width: 1200px;
            margin: 0 auto;
        }

        .content-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 24px;
        }

        .content-title {
            font-size: 24px;
            font-weight: 600;
            color: #1f2937;
        }

        .add-btn {
            background: #374151;
            color: white;
            border: none;
            padding: 10px 16px;
            border-radius: 8px;
            font-size: 14px;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .add-btn:hover {
            background: #1f2937;
        }

        .table-container {
            background: white;
            border-radius: 12px;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }

        .table {
            width: 100%;
            border-collapse: collapse;
        }

        .table th,
        .table td {
            padding: 12px 16px;
            text-align: left;
            border-bottom: 1px solid #e5e7eb;
        }

        .table th {
            background: #f9fafb;
            font-weight: 500;
            color: #374151;
            font-size: 14px;
        }

        .table td {
            font-size: 14px;
            color: #1f2937;
        }

        .table tbody tr:hover {
            background: #f9fafb;
        }

        .password-hidden {
            font-family: monospace;
            letter-spacing: 2px;
        }

        .password-cell {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .action-buttons {
            display: flex;
            gap: 8px;
        }

        .action-btn {
            background: none;
            border: none;
            padding: 4px;
            cursor: pointer;
            border-radius: 4px;
            color: #6b7280;
        }

        .action-btn:hover {
            background: #f3f4f6;
            color: #374151;
        }

        .status-badge {
            padding: 4px 8px;
            border-radius: 12px;
            font-size: 12px;
            font-weight: 500;
        }

        .status-active {
            background: #dcfce7;
            color: #166534;
        }

        .status-current {
            background: #dbeafe;
            color: #1d4ed8;
        }

        .admin-badge {
            background: #dbeafe;
            color: #1d4ed8;
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 12px;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 1000;
        }

        .modal-content {
            background: white;
            border-radius: 12px;
            padding: 24px;
            width: 90%;
            max-width: 500px;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .modal-title {
            font-size: 18px;
            font-weight: 600;
            color: #1f2937;
        }

        .close-btn {
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: #6b7280;
        }

        .form-group {
            margin-bottom: 16px;
        }

        .form-label {
            display: block;
            font-size: 14px;
            font-weight: 500;
            color: #374151;
            margin-bottom: 6px;
        }

        .form-input {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            font-size: 14px;
        }

        .form-input:focus {
            outline: none;
            border-color: #4f46e5;
            box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
        }

        .form-checkbox {
            margin-right: 8px;
        }

        .modal-actions {
            display: flex;
            gap: 12px;
            justify-content: flex-end;
            margin-top: 24px;
        }

        .btn-secondary {
            background: #f3f4f6;
            color: #374151;
            border: none;
            padding: 10px 16px;
            border-radius: 6px;
            font-size: 14px;
            cursor: pointer;
        }

        .btn-primary {
            background: #4f46e5;
            color: white;
            border: none;
            padding: 10px 16px;
            border-radius: 6px;
            font-size: 14px;
            cursor: pointer;
        }

        .btn-primary:hover {
            background: #4338ca;
        }

        .hidden {
            display: none;
        }

        .search-controls {
            background: white;
            padding: 16px;
            border-radius: 8px;
            margin-bottom: 16px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }

        .search-form {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .search-input {
            padding: 8px 12px;
            border: 1px solid #d1d5db;
            border-radius: 6px;
            font-size: 14px;
            width: 300px;
        }

        .search-input:focus {
            outline: none;
            border-color: #4f46e5;
            box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
        }

        .search-btn, .clear-btn {
            padding: 8px 12px;
            border: none;
            border-radius: 6px;
            font-size: 14px;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .search-btn {
            background: #4f46e5;
            color: white;
        }

        .search-btn:hover {
            background: #4338ca;
        }

        .clear-btn {
            background: #f3f4f6;
            color: #374151;
        }

        .clear-btn:hover {
            background: #e5e7eb;
        }

        .page-size-control {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 14px;
            color: #374151;
        }

        .page-size-control select {
            padding: 6px 8px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            font-size: 14px;
        }

        .pagination-container {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 8px;
            margin-top: 20px;
            padding: 16px;
            background: white;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }

        .pagination-info {
            color: #6b7280;
            font-size: 14px;
            margin-right: 16px;
        }

        .pagination-btn {
            padding: 8px 12px;
            border: 1px solid #d1d5db;
            background: white;
            color: #374151;
            border-radius: 6px;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .pagination-btn:hover:not(:disabled) {
            background: #f3f4f6;
            border-color: #9ca3af;
        }

        .pagination-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .pagination-btn.active {
            background: #4f46e5;
            color: white;
            border-color: #4f46e5;
        }

        .pagination-btn.active:hover {
            background: #4338ca;
            border-color: #4338ca;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1 class="header-title">密码管理系统</h1>
        <div class="header-right">
            <span class="welcome-text">欢迎, {{ user.username }}</span>
            <button class="logout-btn">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/>
                </svg>
                退出登录
            </button>
        </div>
    </div>

    <div class="nav-tabs">
        <button class="nav-tab active">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                <path d="M18,8h-1V6c0-2.76-2.24-5-5-5S7,3.24,7,6v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z"/>
            </svg>
            密码管理
        </button>
        {% if user.is_admin %}
        <button class="nav-tab">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                <path d="M16 7c0-2.21-1.79-4-4-4S8 4.79 8 7s1.79 4 4 4 4-1.79 4-4zm-4 6c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/>
            </svg>
            用户管理
        </button>
        {% endif %}
    </div>

    <div class="main-content">
        <!-- 密码管理页面 -->
        <div id="passwords-tab" class="tab-content">
            <div class="content-header">
                <h2 class="content-title">密码管理</h2>
                <button class="add-btn">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
                    </svg>
                    添加密码
                </button>
            </div>

            <!-- 搜索和分页控制区域 -->
            <div class="search-controls">
                <div class="search-form">
                    <input type="text" id="password-search" class="search-input" placeholder="搜索网站、用户名或备注...">
                    <button class="search-btn">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                            <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
                        </svg>
                        搜索
                    </button>
                    <button class="clear-btn">清空</button>
                </div>
                <div class="page-size-control">
                    <label>每页显示:</label>
                    <select id="password-page-size">
                        <option value="5">5条</option>
                        <option value="10" selected>10条</option>
                        <option value="20">20条</option>
                        <option value="50">50条</option>
                        <option value="100">100条</option>
                    </select>
                </div>
            </div>

            <div class="table-container">
                <table class="table">
                    <thead>
                        <tr>
                            <th>序号</th>
                            <th>网站</th>
                            <th>端口</th>
                            <th>用户名</th>
                            <th>密码</th>
                            <th>备注</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody id="passwords-tbody">
                        <!-- 密码数据将通过JavaScript加载 -->
                    </tbody>
                </table>
            </div>

            <!-- 分页控件 -->
            <div class="pagination-container" id="password-pagination">
                <!-- 分页按钮将通过JavaScript生成 -->
            </div>
        </div>

        <!-- 用户管理页面 -->
        {% if user.is_admin %}
        <div id="users-tab" class="tab-content hidden">
            <div class="content-header">
                <h2 class="content-title">用户管理</h2>
                <button class="add-btn">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                        <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
                    </svg>
                    添加用户
                </button>
            </div>

            <!-- 搜索和分页控制区域 -->
            <div class="search-controls">
                <div class="search-form">
                    <input type="text" id="user-search" class="search-input" placeholder="搜索用户名...">
                    <button class="search-btn">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                            <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
                        </svg>
                        搜索
                    </button>
                    <button class="clear-btn">清空</button>
                </div>
                <div class="page-size-control">
                    <label>每页显示:</label>
                    <select id="user-page-size">
                        <option value="5">5条</option>
                        <option value="10" selected>10条</option>
                        <option value="20">20条</option>
                        <option value="50">50条</option>
                        <option value="100">100条</option>
                    </select>
                </div>
            </div>

            <div class="table-container">
                <table class="table">
                    <thead>
                        <tr>
                            <th>用户名</th>
                            <th>密码</th>
                            <th>管理员</th>
                            <th>状态</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody id="users-tbody">
                        <!-- 用户数据将通过JavaScript加载 -->
                    </tbody>
                </table>
            </div>

            <!-- 分页控件 -->
            <div class="pagination-container" id="user-pagination">
                <!-- 分页按钮将通过JavaScript生成 -->
            </div>
        </div>
        {% endif %}
    </div>

    <!-- 添加/编辑密码模态框 -->
    <div id="password-modal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h3 class="modal-title" id="password-modal-title">添加密码</h3>
                <button class="close-btn">×</button>
            </div>
            <form id="password-form">
                <div class="form-group">
                    <label class="form-label">网站</label>
                    <input type="text" class="form-input" id="password-website" required>
                </div>
                <div class="form-group">
                    <label class="form-label">端口</label>
                    <input type="text" class="form-input" id="password-port">
                </div>
                <div class="form-group">
                    <label class="form-label">用户名</label>
                    <input type="text" class="form-input" id="password-username" required>
                </div>
                <div class="form-group">
                    <label class="form-label">密码</label>
                    <input type="password" class="form-input" id="password-password" required>
                </div>
                <div class="form-group">
                    <label class="form-label">备注</label>
                    <input type="text" class="form-input" id="password-note">
                </div>
                <div class="modal-actions">
                    <button type="button" class="btn-secondary">取消</button>
                    <button type="submit" class="btn-primary">保存</button>
                </div>
            </form>
        </div>
    </div>

    <!-- 添加/编辑用户模态框 -->
    {% if user.is_admin %}
    <div id="user-modal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h3 class="modal-title" id="user-modal-title">添加用户</h3>
                <button class="close-btn">×</button>
            </div>
            <form id="user-form">
                <div class="form-group">
                    <label class="form-label">用户名</label>
                    <input type="text" class="form-input" id="user-username" required>
                </div>
                <div class="form-group">
                    <label class="form-label">密码</label>
                    <input type="password" class="form-input" id="user-password" required>
                </div>
                <div class="form-group">
                    <label class="form-label">
                        <input type="checkbox" class="form-checkbox" id="user-is-admin">
                        管理员权限
                    </label>
                </div>
                <div class="modal-actions">
                    <button type="button" class="btn-secondary">取消</button>
                    <button type="submit" class="btn-primary">保存</button>
                </div>
            </form>
        </div>
    </div>
    {% endif %}

    <script>
        let currentPasswordId = null;
        let currentUserId = null;
        const currentUser = {{ user | tojson }};

        // 添加全局变量
        let passwordCurrentPage = 1;
        let passwordPageSize = 10;
        let passwordSearchQuery = '';
        let userCurrentPage = 1;
        let userPageSize = 10;
        let userSearchQuery = '';

        // 页面加载时初始化
        document.addEventListener('DOMContentLoaded', function() {
            loadPasswords();
            {% if user.is_admin %}
            loadUsers();
            {% endif %}

            // 绑定搜索框回车事件
            const passwordSearch = document.getElementById('password-search');
            if (passwordSearch) {
                passwordSearch.addEventListener('keypress', function(e) {
                    if (e.key === 'Enter') {
                        searchPasswords();
                    }
                });
            }

            const userSearch = document.getElementById('user-search');
            if (userSearch) {
                userSearch.addEventListener('keypress', function(e) {
                    if (e.key === 'Enter') {
                        searchUsers();
                    }
                });
            }

            // 绑定密码表单提交事件
            const passwordForm = document.getElementById('password-form');
            if (passwordForm) {
                passwordForm.addEventListener('submit', async function(e) {
                    e.preventDefault();

                    const formData = {
                        website: document.getElementById('password-website').value,
                        port: document.getElementById('password-port').value,
                        username: document.getElementById('password-username').value,
                        password: document.getElementById('password-password').value,
                        note: document.getElementById('password-note').value
                    };

                    try {
                        const url = currentPasswordId ? `/api/passwords/${currentPasswordId}` : '/api/passwords';
                        const method = currentPasswordId ? 'PUT' : 'POST';

                        const response = await fetch(url, {
                            method: method,
                            headers: {
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify(formData)
                        });

                        const result = await response.json();

                        if (result.success) {
                            closePasswordModal();
                            // 保持当前页面和搜索状态
                            loadPasswords(passwordCurrentPage, passwordSearchQuery, passwordPageSize);
                        } else {
                            alert('保存失败');
                        }
                    } catch (error) {
                        console.error('保存失败:', error);
                        alert('保存失败');
                    }
                });
            }

            // 绑定用户表单提交事件
            const userForm = document.getElementById('user-form');
            if (userForm) {
                userForm.addEventListener('submit', async function(e) {
                    e.preventDefault();

                    const formData = {
                        username: document.getElementById('user-username').value,
                        is_admin: document.getElementById('user-is-admin').checked
                    };

                    // 只有在密码不为空时才包含密码字段
                    const passwordValue = document.getElementById('user-password').value;
                    if (passwordValue && passwordValue.trim() !== '') {
                        formData.password = passwordValue.trim();
                    }

                    try {
                        const url = currentUserId ? `/api/users/${currentUserId}` : '/api/users';
                        const method = currentUserId ? 'PUT' : 'POST';

                        const response = await fetch(url, {
                            method: method,
                            headers: {
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify(formData)
                        });

                        if (!response.ok) {
                            throw new Error(`HTTP error! status: ${response.status}`);
                        }

                        const result = await response.json();
                        console.log('API响应:', result); // 调试用

                        if (result.success) {
                            closeUserModal();
                            // 显示成功提示
                            showSuccessMessage(currentUserId ? '用户更新成功!' : '用户添加成功!');
                            // 保持当前页面和搜索状态
                            loadUsers(userCurrentPage, userSearchQuery, userPageSize);
                        } else {
                            alert(result.message || '保存失败');
                        }
                    } catch (error) {
                        console.error('保存失败:', error);
                        alert('网络错误或服务器错误,请重试');
                    }
                });
            }

            // 点击模态框外部关闭
            window.addEventListener('click', function(e) {
                if (e.target.classList.contains('modal')) {
                    e.target.style.display = 'none';
                }
            });
        });

        // 显示/隐藏密码
        function togglePassword(id, password) {
            const element = document.getElementById(`pwd-${id}`);
            if (element.textContent === '&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;') {
                element.textContent = password;
                element.classList.remove('password-hidden');
            } else {
                element.textContent = '&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;';
                element.classList.add('password-hidden');
            }
        }

        // 显示/隐藏用户密码
        async function toggleUserPassword(id, fallbackPassword, userId) {
            const element = document.getElementById(`user-pwd-${id}`);

            if (element.textContent === '&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;') {
                // 如果当前显示的是隐藏状态,尝试获取真实密码
                try {
                    // 首先尝试使用传入的密码
                    if (fallbackPassword && fallbackPassword !== 'N/A' && fallbackPassword !== '&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;') {
                        element.textContent = fallbackPassword;
                        element.classList.remove('password-hidden');
                        return;
                    }

                    // 如果没有密码,尝试从API获取
                    const response = await fetch(`/api/users/${userId}`);
                    if (response.ok) {
                        const result = await response.json();
                        if (result.success && result.password) {
                            element.textContent = result.password;
                            element.classList.remove('password-hidden');
                            return;
                        }
                    }

                    // 如果API调用失败,显示提示信息
                    element.textContent = '无法获取密码';
                    element.classList.remove('password-hidden');

                } catch (error) {
                    console.error('获取用户密码失败:', error);
                    element.textContent = '获取失败';
                    element.classList.remove('password-hidden');
                }
            } else {
                // 隐藏密码
                element.textContent = '&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;';
                element.classList.add('password-hidden');
            }
        }

        // 复制到剪贴板
        async function copyToClipboard(text) {
            try {
                await navigator.clipboard.writeText(text);
                // 可以添加一个简单的提示
                console.log('复制成功');
            } catch (error) {
                console.error('复制失败:', error);
                // 降级方案
                const textArea = document.createElement('textarea');
                textArea.value = text;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
            }
        }

        // 显示添加密码模态框
        function showAddPasswordModal() {
            currentPasswordId = null;
            document.getElementById('password-modal-title').textContent = '添加密码';
            document.getElementById('password-form').reset();
            document.getElementById('password-modal').style.display = 'block';
        }

        // 编辑密码
        async function editPassword(id) {
            currentPasswordId = id;
            document.getElementById('password-modal-title').textContent = '编辑密码';
            // 这里可以预填充数据,暂时简化处理
            document.getElementById('password-modal').style.display = 'block';
        }

        // 关闭密码模态框
        function closePasswordModal() {
            document.getElementById('password-modal').style.display = 'none';
        }

        // 删除密码
        async function deletePassword(id) {
            if (confirm('确定要删除这个密码记录吗?')) {
                try {
                    const response = await fetch(`/api/passwords/${id}`, {
                        method: 'DELETE'
                    });
                    const result = await response.json();

                    if (result.success) {
                        // 保持当前页面和搜索状态
                        loadPasswords(passwordCurrentPage, passwordSearchQuery, passwordPageSize);
                    } else {
                        alert('删除失败');
                    }
                } catch (error) {
                    console.error('删除失败:', error);
                    alert('删除失败');
                }
            }
        }

        // 显示添加用户模态框
        function showAddUserModal() {
            currentUserId = null;
            document.getElementById('user-modal-title').textContent = '添加用户';
            document.getElementById('user-form').reset();

            // 添加用户时密码必填
            const passwordField = document.getElementById('user-password');
            passwordField.required = true;
            passwordField.placeholder = '请输入密码';

            document.getElementById('user-modal').style.display = 'block';
        }

        // 编辑用户
        async function editUser(id, username, isAdmin) {
            currentUserId = id;
            document.getElementById('user-modal-title').textContent = '编辑用户';

            // 预填充数据
            document.getElementById('user-username').value = username;
            document.getElementById('user-is-admin').checked = isAdmin;

            // 编辑时密码字段可以为空(表示不修改密码)
            const passwordField = document.getElementById('user-password');
            passwordField.value = '';
            passwordField.required = false;
            passwordField.placeholder = '留空表示不修改密码';

            document.getElementById('user-modal').style.display = 'block';
        }

        // 删除用户
        async function deleteUser(id) {
            if (confirm('确定要删除这个用户吗?')) {
                try {
                    const response = await fetch(`/api/users/${id}`, {
                        method: 'DELETE'
                    });
                    const result = await response.json();

                    if (result.success) {
                        // 保持当前页面和搜索状态
                        loadUsers(userCurrentPage, userSearchQuery, userPageSize);
                    } else {
                        alert(result.message || '删除失败');
                    }
                } catch (error) {
                    console.error('删除失败:', error);
                    alert('删除失败');
                }
            }
        }

        // 切换标签页
        function switchTab(tabName) {
            // 更新标签按钮状态
            document.querySelectorAll('.nav-tab').forEach(tab => {
                tab.classList.remove('active');
            });
            event.target.classList.add('active');

            // 显示对应内容
            document.querySelectorAll('.tab-content').forEach(content => {
                content.classList.add('hidden');
            });
            document.getElementById(tabName + '-tab').classList.remove('hidden');

            // 加载对应数据
            if (tabName === 'passwords') {
                loadPasswords();
            } else if (tabName === 'users') {
                loadUsers();
            }
        }

        // 退出登录
        function logout() {
            window.location.href = '/logout';
        }

        // 更新加载密码列表函数
        async function loadPasswords(page = 1, search = '', pageSize = 10) {
            try {
                const params = new URLSearchParams({
                    page: page,
                    per_page: pageSize,
                    search: search
                });

                const response = await fetch(`/api/passwords?${params}`);
                const result = await response.json();

                const tbody = document.getElementById('passwords-tbody');
                tbody.innerHTML = '';

                if (result.data.length === 0) {
                    tbody.innerHTML = '<tr><td colspan="7" style="text-align: center; color: #6b7280; padding: 40px;">暂无数据</td></tr>';
                } else {
                    result.data.forEach((password, index) => {
                        const globalIndex = (page - 1) * pageSize + index + 1;
                        const row = document.createElement('tr');
                        row.innerHTML = `
                            <td>${globalIndex}</td>
                            <td>${password.website}</td>
                            <td>${password.port}</td>
                            <td>${password.username}</td>
                            <td>
                                <span class="password-hidden" id="pwd-${password.id}">&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;</span>
                            </td>
                            <td>${password.note}</td>
                            <td>
                                <div class="action-buttons">
                                    <button class="action-btn" title="复制用户名">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                                        </svg>
                                    </button>
                                    <button class="action-btn" title="显示/隐藏密码">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
                                        </svg>
                                    </button>
                                    <button class="action-btn" title="复制密码">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
                                        </svg>
                                    </button>
                                    <button class="action-btn" title="编辑">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
                                        </svg>
                                    </button>
                                    <button class="action-btn" title="删除">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
                                        </svg>
                                    </button>
                                </div>
                            </td>
                        `;
                        tbody.appendChild(row);
                    });
                }

                // 更新分页控件
                updatePasswordPagination(result.pagination);

                // 更新全局状态
                passwordCurrentPage = page;
                passwordPageSize = pageSize;
                passwordSearchQuery = search;

            } catch (error) {
                console.error('加载密码列表失败:', error);
            }
        }

        // 更新加载用户列表函数
        async function loadUsers(page = 1, search = '', pageSize = 10) {
            try {
                const params = new URLSearchParams({
                    page: page,
                    per_page: pageSize,
                    search: search
                });

                const response = await fetch(`/api/users?${params}`);
                const result = await response.json();

                const tbody = document.getElementById('users-tbody');

                tbody.innerHTML = '';

                if (result.data.length === 0) {
                    tbody.innerHTML = '<tr><td colspan="5" style="text-align: center; color: #6b7280; padding: 40px;">暂无数据</td></tr>';
                } else {
                    result.data.forEach(user => {
                        const row = document.createElement('tr');
                        const isCurrentUser = user.id === currentUser.id;

                        const actualPassword = user.password || user.raw_password || 'N/A'; // 根据API返回的字段调整
                        row.innerHTML = `
                            <td>${user.username}</td>
                            <td>
                                <div class="password-cell">
                                    <span class="password-hidden" id="user-pwd-${user.id}">&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;</span>
                                    <button class="action-btn"\\'")}', ${user.id})" title="显示/隐藏密码">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
                                        </svg>
                                    </button>
                                </div>
                            </td>
                            <td>${user.is_admin ? '<span class="admin-badge">是</span>' : '否'}</td>
                            <td>
                                <span class="status-badge ${isCurrentUser ? 'status-current' : 'status-active'}">
                                    ${isCurrentUser ? '当前用户' : '正常'}
                                </span>
                            </td>
                            <td>
                                <div class="action-buttons">
                                    <button class="action-btn" title="编辑">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
                                        </svg>
                                    </button>
                                    ${!isCurrentUser ? `
                                    <button class="action-btn" title="删除">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
                                        </svg>
                                    </button>
                                    ` : ''}
                                </div>
                            </td>
                        `;
                        tbody.appendChild(row);
                    });
                }
                
                // 更新分页控件
                updateUserPagination(result.pagination);
                
                // 更新全局状态
                userCurrentPage = page;
                userPageSize = pageSize;
                userSearchQuery = search;
                
            } catch (error) {
                console.error('加载用户列表失败:', error);
            }
        }

        // 搜索和分页相关函数
        function searchPasswords() {
            const searchQuery = document.getElementById('password-search').value;
            loadPasswords(1, searchQuery, passwordPageSize);
        }

        function clearPasswordSearch() {
            document.getElementById('password-search').value = '';
            loadPasswords(1, '', passwordPageSize);
        }

        function changePasswordPageSize() {
            const pageSize = parseInt(document.getElementById('password-page-size').value);
            loadPasswords(1, passwordSearchQuery, pageSize);
        }

        function searchUsers() {
            const searchQuery = document.getElementById('user-search').value;
            loadUsers(1, searchQuery, userPageSize);
        }

        function clearUserSearch() {
            document.getElementById('user-search').value = '';
            loadUsers(1, '', userPageSize);
        }

        function changeUserPageSize() {
            const pageSize = parseInt(document.getElementById('user-page-size').value);
            loadUsers(1, userSearchQuery, pageSize);
        }

        // 分页控件更新函数
        function updatePasswordPagination(pagination) {
            const container = document.getElementById('password-pagination');
            if (!pagination || pagination.total_pages <= 1) {
                container.innerHTML = '';
                return;
            }
            
            let html = `
                <div class="pagination-info">
                    共 ${pagination.total_count} 条记录,第 ${pagination.current_page} / ${pagination.total_pages} 页
                </div>
            `;
            
            // 上一页按钮
            html += `
                <button class="pagination-btn" ${!pagination.has_prev ? 'disabled' : ''} 
                       >
                    上一页
                </button>
            `;
            
            // 页码按钮
            const startPage = Math.max(1, pagination.current_page - 2);
            const endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
            
            if (startPage > 1) {
                html += `<button class="pagination-btn">1</button>`;
                if (startPage > 2) {
                    html += `<span>...</span>`;
                }
            }
            
            for (let i = startPage; i <= endPage; i++) {
                html += `
                    <button class="pagination-btn ${i === pagination.current_page ? 'active' : ''}" 
                           >
                        ${i}
                    </button>
                `;
            }
            
            if (endPage < pagination.total_pages) {
                if (endPage < pagination.total_pages - 1) {
                    html += `<span>...</span>`;
                }
                html += `<button class="pagination-btn">${pagination.total_pages}</button>`;
            }
            
            // 下一页按钮
            html += `
                <button class="pagination-btn" ${!pagination.has_next ? 'disabled' : ''} 
                       >
                    下一页
                </button>
            `;
            
            container.innerHTML = html;
        }

        function updateUserPagination(pagination) {
            const container = document.getElementById('user-pagination');
            if (!pagination || pagination.total_pages <= 1) {
                container.innerHTML = '';
                return;
            }
            
            let html = `
                <div class="pagination-info">
                    共 ${pagination.total_count} 条记录,第 ${pagination.current_page} / ${pagination.total_pages} 页
                </div>
            `;
            
            // 上一页按钮
            html += `
                <button class="pagination-btn" ${!pagination.has_prev ? 'disabled' : ''} 
                       >
                    上一页
                </button>
            `;
            
            // 页码按钮
            const startPage = Math.max(1, pagination.current_page - 2);
            const endPage = Math.min(pagination.total_pages, pagination.current_page + 2);
            
            if (startPage > 1) {
                html += `<button class="pagination-btn">1</button>`;
                if (startPage > 2) {
                    html += `<span>...</span>`;
                }
            }
            
            for (let i = startPage; i <= endPage; i++) {
                html += `
                    <button class="pagination-btn ${i === pagination.current_page ? 'active' : ''}" 
                           >
                        ${i}
                    </button>
                `;
            }
            
            if (endPage < pagination.total_pages) {
                if (endPage < pagination.total_pages - 1) {
                    html += `<span>...</span>`;
                }
                html += `<button class="pagination-btn">${pagination.total_pages}</button>`;
            }
            
            // 下一页按钮
            html += `
                <button class="pagination-btn" ${!pagination.has_next ? 'disabled' : ''} 
                       >
                    下一页
                </button>
            `;
            
            container.innerHTML = html;
        }

        // 显示成功提示
        function showSuccessMessage(message) {
            // 创建提示元素
            const toast = document.createElement('div');
            toast.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: #10b981;
                color: white;
                padding: 12px 20px;
                border-radius: 8px;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
                z-index: 10000;
                font-size: 14px;
                font-weight: 500;
            `;
            toast.textContent = message;
            
            document.body.appendChild(toast);
            
            // 3秒后自动消失
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 3000);
        }

        // 关闭用户模态框
        function closeUserModal() {
            const modal = document.getElementById('user-modal');
            if (modal) {
                modal.style.display = 'none';
                // 重置表单
                document.getElementById('user-form').reset();
                // 清空当前用户ID
                currentUserId = null;
            }
        }
    </script>
</body>
</html>


登录界面代码如下:
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>密码管理系统</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }

        .login-container {
            background: white;
            border-radius: 12px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            padding: 40px;
            width: 100%;
            max-width: 400px;
            text-align: center;
        }

        .lock-icon {
            width: 60px;
            height: 60px;
            background: #4f46e5;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0 auto 24px;
        }

        .lock-icon svg {
            width: 24px;
            height: 24px;
            fill: white;
        }

        .title {
            font-size: 24px;
            font-weight: 600;
            color: #1f2937;
            margin-bottom: 8px;
        }

        .subtitle {
            color: #6b7280;
            font-size: 14px;
            margin-bottom: 32px;
        }

        .form-group {
            margin-bottom: 20px;
            text-align: left;
        }

        .form-label {
            display: block;
            font-size: 14px;
            font-weight: 500;
            color: #374151;
            margin-bottom: 6px;
        }

        .form-input {
            width: 100%;
            padding: 12px 16px;
            border: 1px solid #d1d5db;
            border-radius: 8px;
            font-size: 14px;
            transition: border-color 0.2s, box-shadow 0.2s;
        }

        .form-input:focus {
            outline: none;
            border-color: #4f46e5;
            box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
        }

        .login-btn {
            width: 100%;
            background: #374151;
            color: white;
            border: none;
            padding: 12px;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: background-color 0.2s;
            margin-bottom: 24px;
        }

        .login-btn:hover {
            background: #1f2937;
        }

        .login-btn:disabled {
            background: #9ca3af;
            cursor: not-allowed;
        }

        .default-account {
            color: #4f46e5;
            font-size: 14px;
        }

        .error-message {
            color: #ef4444;
            font-size: 14px;
            margin-top: 10px;
            display: none;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <div class="lock-icon">
            <svg viewBox="0 0 24 24">
                <path d="M18,8h-1V6c0-2.76-2.24-5-5-5S7,3.24,7,6v2H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10C20,8.9,19.1,8,18,8z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z M15.1,8H8.9V6c0-1.71,1.39-3.1,3.1-3.1s3.1,1.39,3.1,3.1V8z"/>
            </svg>
        </div>
        
        <h1 class="title">密码管理系统</h1>
        <p class="subtitle">请登录以访问您的密码库</p>
        
        <form id="loginForm">
            <div class="form-group">
                <label class="form-label">用户名</label>
                <input type="text" class="form-input" id="username" placeholder="请输入用户名" required>
            </div>
            
            <div class="form-group">
                <label class="form-label">密码</label>
                <input type="password" class="form-input" id="password" placeholder="请输入密码" required>
            </div>
            
            <button type="submit" class="login-btn" id="loginBtn">登录</button>
            
            <div class="error-message" id="errorMessage"></div>
        </form>
        
        <p class="default-account">默认账号: admin / admin</p>
    </div>

    <script>
        document.getElementById('loginForm').addEventListener('submit', async function(e) {
            e.preventDefault();
            
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
            const loginBtn = document.getElementById('loginBtn');
            const errorMessage = document.getElementById('errorMessage');
            
            loginBtn.disabled = true;
            loginBtn.textContent = '登录中...';
            errorMessage.style.display = 'none';
            
            try {
                const response = await fetch('/login', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ username, password })
                });
                
                const result = await response.json();
                
                if (result.success) {
                    window.location.href = '/dashboard';
                } else {
                    errorMessage.textContent = result.message || '登录失败';
                    errorMessage.style.display = 'block';
                }
            } catch (error) {
                errorMessage.textContent = '网络错误,请重试';
                errorMessage.style.display = 'block';
            } finally {
                loginBtn.disabled = false;
                loginBtn.textContent = '登录';
            }
        });
    </script>
</body>
</html>


app.py
[Python] 纯文本查看 复制代码
from flask import Flask, render_template, request, jsonify, session, redirect, url_for
from database import DatabaseManager
import os

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'
db = DatabaseManager()

@app.route('/')
def index():
    if 'user' in session:
        return redirect(url_for('dashboard'))
    return render_template('login.html')

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    
    user = db.authenticate_user(username, password)
    if user:
        session['user'] = user
        return jsonify({'success': True})
    else:
        return jsonify({'success': False, 'message': '用户名或密码错误'})

@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect(url_for('index'))

@app.route('/dashboard')
def dashboard():
    if 'user' not in session:
        return redirect(url_for('index'))
    return render_template('dashboard.html', user=session['user'])

@app.route('/api/passwords')
def get_passwords():
    if 'user' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # 获取查询参数
    search_query = request.args.get('search', '')
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 10))
    
    # 限制每页显示数量
    per_page = min(max(per_page, 5), 100)  # 限制在5-100之间
    
    result = db.get_user_passwords(session['user']['id'], search_query, page, per_page)
    return jsonify(result)

@app.route('/api/passwords', methods=['POST'])
def add_password():
    if 'user' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    data = request.get_json()
    success = db.add_password(
        session['user']['id'],
        data['website'],
        data.get('port', ''),
        data['username'],
        data['password'],
        data.get('note', '')
    )
    
    return jsonify({'success': success})

@app.route('/api/passwords/<int:password_id>', methods=['PUT'])
def update_password(password_id):
    if 'user' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    data = request.get_json()
    success = db.update_password(
        password_id,
        data['website'],
        data.get('port', ''),
        data['username'],
        data['password'],
        data.get('note', '')
    )
    
    return jsonify({'success': success})

@app.route('/api/passwords/<int:password_id>', methods=['DELETE'])
def delete_password(password_id):
    if 'user' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    success = db.delete_password(password_id)
    return jsonify({'success': success})

@app.route('/api/users')
def get_users():
    if 'user' not in session or not session['user']['is_admin']:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # 获取查询参数
    search_query = request.args.get('search', '')
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 10))
    
    # 限制每页显示数量
    per_page = min(max(per_page, 5), 100)  # 限制在5-100之间
    
    result = db.get_all_users(search_query, page, per_page)
    return jsonify(result)

@app.route('/api/users', methods=['POST'])
def add_user():
    if 'user' not in session or not session['user']['is_admin']:
        return jsonify({'error': 'Unauthorized'}), 401
    
    data = request.get_json()
    success = db.add_user(
        data['username'],
        data['password'],
        data.get('is_admin', False)
    )
    
    return jsonify({'success': success})

@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
    if 'user' not in session or not session['user']['is_admin']:
        return jsonify({'error': 'Unauthorized'}), 401
    
    data = request.get_json()
    
    try:
        success = db.update_user(
            user_id,
            data['username'],
            data.get('password'),  # 这里可能为None或空字符串
            data.get('is_admin', False)
        )
        
        if success:
            return jsonify({'success': True, 'message': '用户更新成功'})
        else:
            return jsonify({'success': False, 'message': '用户更新失败'})
    except Exception as e:
        print(f"更新用户时出错: {e}")
        return jsonify({'success': False, 'message': '服务器错误'})

@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
    if 'user' not in session or not session['user']['is_admin']:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # 不能删除当前登录用户
    if user_id == session['user']['id']:
        return jsonify({'success': False, 'message': '不能删除当前登录用户'})
    
    success = db.delete_user(user_id)
    return jsonify({'success': success})

if __name__ == '__main__':
    # 创建templates目录
    os.makedirs('templates', exist_ok=True)
    os.makedirs('static', exist_ok=True)
    
    app.run(debug=True, host='0.0.0.0', port=5000)

database.py
[Python] 纯文本查看 复制代码
import hashlib
import sqlite3


class DatabaseManager:
    def __init__(self, db_path="password.db"):
        self.db_path = db_path
        self.init_database()
    
    def init_database(self):
        """初始化数据库表"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 创建用户表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                password TEXT NOT NULL,
                is_admin INTEGER DEFAULT 0,
                status TEXT DEFAULT 'active',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        # 创建密码表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS passwords (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id INTEGER,
                website TEXT NOT NULL,
                port TEXT DEFAULT '-',
                username TEXT NOT NULL,
                password TEXT NOT NULL,
                note TEXT DEFAULT '-',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_id) REFERENCES users (id)
            )
        ''')
        
        # 插入默认管理员账户
        admin_password = hashlib.sha256("admin".encode()).hexdigest()
        cursor.execute('''
            INSERT OR IGNORE INTO users (username, password, is_admin, status)
            VALUES (?, ?, 1, 'active')
        ''', ("admin", admin_password))
        
        conn.commit()
        conn.close()
    
    def authenticate_user(self, username, password):
        """用户认证"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        hashed_password = hashlib.sha256(password.encode()).hexdigest()
        cursor.execute('''
            SELECT id, username, is_admin FROM users 
            WHERE username = ? AND password = ? AND status = 'active'
        ''', (username, hashed_password))
        
        user = cursor.fetchone()
        conn.close()
        
        if user:
            return {
                'id': user[0],
                'username': user[1],
                'is_admin': bool(user[2])
            }
        return None
    
    def get_all_users(self, search_query=None, page=1, per_page=10):
        """获取所有用户(支持搜索和分页)"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 构建基础查询
        base_query = '''
            SELECT id, username, password, is_admin, status FROM users
        '''
        params = []
        
        # 添加搜索条件
        if search_query and search_query.strip():
            base_query += ' WHERE username LIKE ?'
            params.append(f'%{search_query.strip()}%')
        
        # 获取总数
        count_query = f"SELECT COUNT(*) FROM ({base_query}) as filtered"
        cursor.execute(count_query, params)
        total_count = cursor.fetchone()[0]
        
        # 添加排序和分页
        base_query += ' ORDER BY id LIMIT ? OFFSET ?'
        offset = (page - 1) * per_page
        params.extend([per_page, offset])
        
        cursor.execute(base_query, params)
        users = cursor.fetchall()
        conn.close()
        
        # 计算分页信息
        total_pages = (total_count + per_page - 1) // per_page
        
        return {
            'data': [
                {
                    'id': user[0],
                    'username': user[1],
                    'password': user[2],
                    'is_admin': bool(user[3]),
                    'status': user[4]
                }
                for user in users
            ],
            'pagination': {
                'current_page': page,
                'per_page': per_page,
                'total_count': total_count,
                'total_pages': total_pages,
                'has_prev': page > 1,
                'has_next': page < total_pages
            }
        }
    
    def add_user(self, username, password, is_admin=False):
        """添加用户"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        hashed_password = hashlib.sha256(password.encode()).hexdigest()
        
        try:
            cursor.execute('''
                INSERT INTO users (username, password, is_admin, status)
                VALUES (?, ?, ?, 'active')
            ''', (username, hashed_password, int(is_admin)))
            
            conn.commit()
            return True
        except sqlite3.IntegrityError:
            return False
        finally:
            conn.close()
    
    def update_user(self, user_id, username, password=None, is_admin=False):
        """更新用户"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        try:
            if password and password.strip():
                # 如果提供了密码且不为空,则更新密码
                hashed_password = hashlib.sha256(password.encode()).hexdigest()
                cursor.execute('''
                    UPDATE users SET username = ?, password = ?, is_admin = ?
                    WHERE id = ?
                ''', (username, hashed_password, int(is_admin), user_id))
            else:
                # 如果没有提供密码或密码为空,则不更新密码
                cursor.execute('''
                    UPDATE users SET username = ?, is_admin = ?
                    WHERE id = ?
                ''', (username, int(is_admin), user_id))
        
            conn.commit()
            return True
        except sqlite3.Error as e:
            print(f"数据库错误: {e}")
            return False
        except Exception as e:
            print(f"更新用户时出错: {e}")
            return False
        finally:
            conn.close()
    
    def delete_user(self, user_id):
        """删除用户"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 删除用户的所有密码记录
        cursor.execute('DELETE FROM passwords WHERE user_id = ?', (user_id,))
        # 删除用户
        cursor.execute('DELETE FROM users WHERE id = ?', (user_id,))
        
        conn.commit()
        conn.close()
        return True
    
    def get_user_passwords(self, user_id, search_query=None, page=1, per_page=10):
        """获取用户的密码记录(支持搜索和分页)"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 构建基础查询
        base_query = '''
            SELECT id, website, port, username, password, note
            FROM passwords WHERE user_id = ?
        '''
        params = [user_id]
        
        # 添加搜索条件
        if search_query and search_query.strip():
            search_condition = '''
                AND (website LIKE ? OR username LIKE ? OR note LIKE ?)
            '''
            base_query += search_condition
            search_param = f'%{search_query.strip()}%'
            params.extend([search_param, search_param, search_param])
        
        # 获取总数
        count_query = f"SELECT COUNT(*) FROM ({base_query}) as filtered"
        cursor.execute(count_query, params)
        total_count = cursor.fetchone()[0]
        
        # 添加排序和分页
        base_query += ' ORDER BY id DESC LIMIT ? OFFSET ?'
        offset = (page - 1) * per_page
        params.extend([per_page, offset])
        
        cursor.execute(base_query, params)
        passwords = cursor.fetchall()
        conn.close()
        
        # 计算分页信息
        total_pages = (total_count + per_page - 1) // per_page
        
        return {
            'data': [
                {
                    'id': pwd[0],
                    'website': pwd[1],
                    'port': pwd[2],
                    'username': pwd[3],
                    'password': pwd[4],
                    'note': pwd[5]
                }
                for pwd in passwords
            ],
            'pagination': {
                'current_page': page,
                'per_page': per_page,
                'total_count': total_count,
                'total_pages': total_pages,
                'has_prev': page > 1,
                'has_next': page < total_pages
            }
        }
    
    def add_password(self, user_id, website, port, username, password, note):
        """添加密码记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT INTO passwords (user_id, website, port, username, password, note)
            VALUES (?, ?, ?, ?, ?, ?)
        ''', (user_id, website, port or '-', username, password, note or '-'))
        
        conn.commit()
        conn.close()
        return True
    
    def update_password(self, password_id, website, port, username, password, note):
        """更新密码记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            UPDATE passwords 
            SET website = ?, port = ?, username = ?, password = ?, note = ?
            WHERE id = ?
        ''', (website, port or '-', username, password, note or '-', password_id))
        
        conn.commit()
        conn.close()
        return True
    
    def delete_password(self, password_id):
        """删除密码记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('DELETE FROM passwords WHERE id = ?', (password_id,))
        
        conn.commit()
        conn.close()
        return True

# 初始化数据库
if __name__ == "__main__":
    db = DatabaseManager()
    print("数据库初始化完成")

run.py
[Python] 纯文本查看 复制代码
#!/usr/bin/env python3
"""
密码管理系统启动脚本
使用方法: python run.py
"""

import os
import sys
from app import app
from database import DatabaseManager

def main():
    print("=" * 50)
    print("密码管理系统 v1.0")
    print("=" * 50)
    
    # 初始化数据库
    print("正在初始化数据库...")
    db = DatabaseManager()
    print("数据库初始化完成!")
    
    print("\n系统信息:")
    print(f"- 默认管理员账号: admin")
    print(f"- 默认管理员密码: admin")
    print(f"- 访问地址: http://localhost:5000")
    print(f"- 数据库文件: {os.path.abspath('password_manager.db')}")
    
    print("\n正在启动服务器...")
    print("按 Ctrl+C 停止服务器")
    print("=" * 50)
    
    try:
        app.run(debug=True, host='0.0.0.0', port=5000)
    except KeyboardInterrupt:
        print("\n服务器已停止")
        sys.exit(0)

if __name__ == "__main__":
    main()


exe下载地址:链接: https://pan.baidu.com/s/1xfybDBTUhwO92hsRDo2dTw?pwd=wcb9 提取码: wcb9

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
outdoorreadbook + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

precise 发表于 2025-11-28 21:48
求一个exe文件下载链接
影风 发表于 2025-11-29 21:31
 楼主| yhx5773489 发表于 2025-12-1 08:58
影风 发表于 2025-11-29 21:31
求一个exe文件下载链接

我没放云盘 你直接下载源代码后 用一条命令就能打包成exe 很简单
 楼主| yhx5773489 发表于 2025-12-1 09:08
precise 发表于 2025-11-28 21:48
求一个exe文件下载链接

文末有链接
 楼主| yhx5773489 发表于 2025-12-1 09:09
影风 发表于 2025-11-29 21:31
求一个exe文件下载链接

文末有链接
zhuangshuaige 发表于 2026-1-2 08:50
这些是楼主自己写的吗,如果编程小白想拿这个练手学习代码,可以吗
 楼主| yhx5773489 发表于 2026-2-2 16:54
zhuangshuaige 发表于 2026-1-2 08:50
这些是楼主自己写的吗,如果编程小白想拿这个练手学习代码,可以吗

嗯 自己写的 可以
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-2-20 15:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表