diff --git a/app.py b/app.py index 7fd4973..62ae70b 100644 --- a/app.py +++ b/app.py @@ -13,6 +13,7 @@ app.secret_key = str(uuid.uuid4()) # 配置文件路径 CONFIG_FILE = 'config/config.json' SQUID_PASSWD_FILE = 'config/squid_passwd' +USERS_FILE = 'config/users.json' # 新增用户信息存储文件 # 加载配置 @@ -42,7 +43,7 @@ def save_config(config): class User: def __init__(self, name, password, is_active=True): self.name = name - self.password = password + self.password = password # 这里存储明文密码 self.is_active = is_active @@ -74,7 +75,10 @@ def read_squid_file(): parts = line.split(':', 1) if len(parts) == 2: - users.append(User(parts[0], parts[1], is_active)) + # 从users.json中获取明文密码 + users_data = load_users_data() + plain_password = next((u['password'] for u in users_data if u['username'] == parts[0]), parts[1]) + users.append(User(parts[0], plain_password, is_active)) except FileNotFoundError: pass return users @@ -87,12 +91,33 @@ def write_squid_file(users): f.write(line) +def load_users_data(): + """加载用户数据""" + try: + with open(USERS_FILE, 'r') as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return [] + + +def save_users_to_json(users): + """保存用户信息到JSON文件""" + users_data = [{'username': u.name, 'password': u.password, 'active': u.is_active} for u in users] + with open(USERS_FILE, 'w') as f: + json.dump(users_data, f, indent=4) + + def create_user(username, password): users = read_squid_file() if any(u.name == username for u in users): return False try: + # 先保存明文密码到users.json + users.append(User(username, password, True)) + save_users_to_json(users) + + # 然后创建加密密码 subprocess.run(['htpasswd', '-b', SQUID_PASSWD_FILE, username, password], check=True) return True except subprocess.CalledProcessError: @@ -109,7 +134,7 @@ def index(): proxy_address=config['proxy_address'], proxy_port=config['proxy_port']) -# 在 clients 路由中增加分页逻辑 + @app.route('/clients') @basic_auth_required def clients(): @@ -123,13 +148,14 @@ def clients(): end = start + per_page paginated_users = users[start:end] + config = load_config() + return render_template('clients.html', users=paginated_users, page=page, - total_pages=total_pages) - - -# 其他代码保持不变... + total_pages=total_pages, + proxy_address=config['proxy_address'], + proxy_port=config['proxy_port']) @app.route('/settings') @@ -138,6 +164,7 @@ def settings(): config = load_config() return render_template('settings.html', config=config) + @app.route('/update_settings', methods=['POST']) @basic_auth_required def update_settings(): @@ -154,7 +181,7 @@ def update_settings(): config['proxy_port'] = proxy_port save_config(config) - flash('设置已成功保存!', 'success') # 添加成功提示 + flash('设置已成功保存!', 'success') return redirect(url_for('settings')) @@ -167,6 +194,7 @@ def toggle_user(username): user.is_active = not user.is_active break write_squid_file(users) + save_users_to_json(users) # 更新users.json return '', 200 @@ -175,6 +203,7 @@ def toggle_user(username): def delete_user(username): users = [u for u in read_squid_file() if u.name != username] write_squid_file(users) + save_users_to_json(users) # 更新users.json return '', 200 @@ -189,6 +218,15 @@ def save_user(): return jsonify({'error': 'Username and password required'}), 400 try: + # 更新明文密码 + users = read_squid_file() + for user in users: + if user.name == username: + user.password = password + break + save_users_to_json(users) + + # 更新加密密码 subprocess.run(['htpasswd', '-b', SQUID_PASSWD_FILE, username, password], check=True) return '', 200 except subprocess.CalledProcessError: @@ -211,6 +249,23 @@ def handle_create_user(): return jsonify({'error': 'User already exists'}), 409 +@app.route('/copy_proxy/', methods=['POST']) +@basic_auth_required +def copy_proxy(username): + config = load_config() + users = read_squid_file() + user = next((u for u in users if u.name == username), None) + + if not user: + return jsonify({'error': 'User not found'}), 404 + + proxy_url = f"http://{user.name}:{user.password}@{config['proxy_address']}:{config['proxy_port']}" + return jsonify({ + 'message': 'Proxy URL copied to clipboard (simulated)', + 'proxy_url': proxy_url + }) + + @app.route('/logout') def logout(): return ('', 401, {'WWW-Authenticate': 'Basic realm="Authorization Required"'}) @@ -220,4 +275,4 @@ if __name__ == '__main__': if not os.path.exists('config'): os.makedirs('config') load_config() # 初始化配置文件 - app.run(host='0.0.0.0', port=8080, debug=True) \ No newline at end of file + app.run(host='0.0.0.0', port=8080, debug=True) diff --git a/requirements.txt b/requirements.txt index 7883f5b..5fcb508 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -Flask==2.3.2 \ No newline at end of file +Flask==2.3.2 +pyperclip diff --git a/static/styles.css b/static/styles.css index a702c1a..f64047a 100644 --- a/static/styles.css +++ b/static/styles.css @@ -483,4 +483,4 @@ input:checked + .slider:before { flex-direction: column; gap: 5px; } -} \ No newline at end of file +} diff --git a/templates/clients.html b/templates/clients.html index 6e1d792..af0b3de 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -27,6 +27,9 @@ + @@ -181,6 +184,26 @@ document.getElementById('createModal').style.display = 'none'; }); + // 复制代理地址 + document.querySelectorAll('.copy-btn').forEach(btn => { + btn.addEventListener('click', function() { + const username = this.dataset.username; + fetch(`/copy_proxy/${username}`, { method: 'POST' }) + .then(response => response.json()) + .then(data => { + // 创建一个临时的textarea元素来复制文本 + const textarea = document.createElement('textarea'); + textarea.value = data.proxy_url; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + + alert('代理地址已复制到剪贴板: ' + data.proxy_url); + }); + }); + }); + // 退出系统 document.getElementById('logoutBtn').addEventListener('click', function() { if(confirm('确定要退出系统吗?')) { @@ -190,13 +213,39 @@ } }); }); - // 增加导航按钮事件 - document.getElementById('logoutBtn').addEventListener('click', function() { - if(confirm('确定要退出系统吗?')) { - fetch('/logout').then(() => { - window.location.href = '/'; - }); - } - }); -{% endblock %} \ No newline at end of file +{% endblock %} + +{% block styles %} + +{% endblock %}