Compare commits
2 Commits
f7310541e1
...
72d03723cd
| Author | SHA1 | Date | |
|---|---|---|---|
| 72d03723cd | |||
| 056d91b7ad |
73
app.py
73
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/<username>', 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)
|
||||
app.run(host='0.0.0.0', port=8080, debug=True)
|
||||
|
||||
@ -1 +1,2 @@
|
||||
Flask==2.3.2
|
||||
Flask==2.3.2
|
||||
pyperclip
|
||||
|
||||
@ -483,4 +483,4 @@ input:checked + .slider:before {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<button class="btn-primary edit-btn">编辑</button>
|
||||
<button class="btn-success copy-btn" data-username="{{ user.name }}">
|
||||
<i class="icon-copy"></i> 复制代理
|
||||
</button>
|
||||
<button class="btn-danger delete-btn">删除</button>
|
||||
</div>
|
||||
</td>
|
||||
@ -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 = '/';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<style>
|
||||
/* 添加复制按钮的绿色样式 */
|
||||
.btn-success {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.btn-success:active {
|
||||
background-color: #1e7e34;
|
||||
}
|
||||
|
||||
.icon-copy {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/20000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath 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'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user