好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 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 === '••••••••') {
element.textContent = password;
element.classList.remove('password-hidden');
} else {
element.textContent = '••••••••';
element.classList.add('password-hidden');
}
}
// 显示/隐藏用户密码
async function toggleUserPassword(id, fallbackPassword, userId) {
const element = document.getElementById(`user-pwd-${id}`);
if (element.textContent === '••••••••') {
// 如果当前显示的是隐藏状态,尝试获取真实密码
try {
// 首先尝试使用传入的密码
if (fallbackPassword && fallbackPassword !== 'N/A' && fallbackPassword !== '••••••••') {
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 = '••••••••';
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}">••••••••</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}">••••••••</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 |
免费评分
-
查看全部评分
|