sing-box 多用户管理与流量监控完整方案
本教程详细介绍如何设置多用户系统,监控每个用户的流量使用情况,并实现用量限制。
目录
多用户配置方案
方案一:sing-box 原生多用户(简单)
sing-box 支持在单个入站中配置多个用户:
{
"inbounds": [
{
"type": "trojan",
"tag": "trojan-in",
"listen": "::",
"listen_port": 443,
"users": [
{
"name": "user1",
"password": "password1"
},
{
"name": "user2",
"password": "password2"
},
{
"name": "user3",
"password": "password3"
}
],
"tls": {
"enabled": true,
"server_name": "your-domain.com",
"certificate_path": "/path/to/cert.pem",
"key_path": "/path/to/key.pem"
}
}
]
}限制:原生方式不支持单独统计每个用户的流量。
方案二:多端口方案(推荐)
为每个用户分配独立端口,便于流量统计:
{
"inbounds": [
{
"type": "trojan",
"tag": "user1-trojan",
"listen": "::",
"listen_port": 8001,
"users": [
{
"name": "user1",
"password": "user1_password"
}
],
"tls": {
"enabled": true,
"server_name": "your-domain.com",
"certificate_path": "/path/to/cert.pem",
"key_path": "/path/to/key.pem"
}
},
{
"type": "trojan",
"tag": "user2-trojan",
"listen": "::",
"listen_port": 8002,
"users": [
{
"name": "user2",
"password": "user2_password"
}
],
"tls": {
"enabled": true,
"server_name": "your-domain.com",
"certificate_path": "/path/to/cert.pem",
"key_path": "/path/to/key.pem"
}
}
]
}方案三:使用 X-UI 管理面板(最方便)
X-UI 是一个功能强大的多协议代理管理面板,支持:
- 多用户管理
- 流量统计
- 用量限制
- 到期时间设置
- Web 界面管理
安装 X-UI
# 一键安装脚本
bash <(curl -Ls https://raw.githubusercontent.com/vaxilu/x-ui/master/install.sh)
# 或者使用 3X-UI(更新的分支)
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)X-UI 特性
- 用户管理:创建、编辑、删除用户
- 流量限制:设置每个用户的流量上限
- 时间限制:设置账号到期时间
- 流量统计:实时查看每个用户的流量使用
- 多协议支持:VMess、VLESS、Trojan、Shadowsocks
- 订阅管理:为每个用户生成独立订阅链接
流量统计与监控
方法一:使用 iptables 统计流量
#!/bin/bash
# traffic-monitor.sh - 基于端口的流量统计
# 创建 iptables 链
create_chains() {
iptables -N TRAFFIC_IN 2>/dev/null || true
iptables -N TRAFFIC_OUT 2>/dev/null || true
# 添加到 INPUT 和 OUTPUT 链
iptables -I INPUT -j TRAFFIC_IN
iptables -I OUTPUT -j TRAFFIC_OUT
}
# 为每个用户添加规则
add_user_rules() {
local user=$1
local port=$2
# 入站流量
iptables -A TRAFFIC_IN -p tcp --dport $port -m comment --comment "$user-in"
# 出站流量
iptables -A TRAFFIC_OUT -p tcp --sport $port -m comment --comment "$user-out"
}
# 获取用户流量
get_user_traffic() {
local user=$1
# 获取入站流量(字节)
local in_bytes=$(iptables -nvxL TRAFFIC_IN | grep "$user-in" | awk '{print $2}')
# 获取出站流量(字节)
local out_bytes=$(iptables -nvxL TRAFFIC_OUT | grep "$user-out" | awk '{print $2}')
echo "用户: $user"
echo "下载: $(numfmt --to=iec-i --suffix=B $in_bytes)"
echo "上传: $(numfmt --to=iec-i --suffix=B $out_bytes)"
echo "总计: $(numfmt --to=iec-i --suffix=B $((in_bytes + out_bytes)))"
}
# 重置用户流量统计
reset_user_traffic() {
local user=$1
iptables -Z TRAFFIC_IN
iptables -Z TRAFFIC_OUT
}
# 初始化
create_chains
# 添加用户规则
add_user_rules "user1" 8001
add_user_rules "user2" 8002
add_user_rules "user3" 8003
# 查看所有用户流量
for user in user1 user2 user3; do
get_user_traffic $user
echo "---"
done方法二:使用 sing-box 统计 API
sing-box 提供了统计 API,可以获取连接和流量信息:
#!/usr/bin/env python3
# traffic_stats.py - sing-box 流量统计
import requests
import json
import sqlite3
from datetime import datetime
import time
class TrafficMonitor:
def __init__(self, api_url="http://127.0.0.1:9090", api_secret="your-secret"):
self.api_url = api_url
self.headers = {"Authorization": f"Bearer {api_secret}"}
self.init_database()
def init_database(self):
"""初始化数据库"""
self.conn = sqlite3.connect('traffic.db')
self.cursor = self.conn.cursor()
# 创建用户表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
port INTEGER,
traffic_limit BIGINT DEFAULT 0, -- 流量限制(字节)
traffic_used BIGINT DEFAULT 0, -- 已用流量(字节)
expire_date DATE, -- 到期时间
status TEXT DEFAULT 'active', -- active/suspended/expired
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 创建流量记录表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS traffic_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
upload BIGINT,
download BIGINT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (username) REFERENCES users(username)
)
''')
self.conn.commit()
def add_user(self, username, password, port, traffic_limit_gb=100, expire_days=30):
"""添加用户"""
traffic_limit = traffic_limit_gb * 1024 * 1024 * 1024 # 转换为字节
expire_date = datetime.now().timestamp() + (expire_days * 86400)
try:
self.cursor.execute('''
INSERT INTO users (username, password, port, traffic_limit, expire_date)
VALUES (?, ?, ?, ?, ?)
''', (username, password, port, traffic_limit, expire_date))
self.conn.commit()
print(f"用户 {username} 添加成功")
return True
except sqlite3.IntegrityError:
print(f"用户 {username} 已存在")
return False
def get_connections(self):
"""获取当前连接"""
response = requests.get(f"{self.api_url}/connections", headers=self.headers)
if response.status_code == 200:
return response.json().get('connections', [])
return []
def update_traffic(self):
"""更新流量统计"""
connections = self.get_connections()
# 按用户统计流量
user_traffic = {}
for conn in connections:
# 根据端口识别用户
port = conn.get('metadata', {}).get('destinationPort')
user = self.get_user_by_port(port)
if user:
if user not in user_traffic:
user_traffic[user] = {'upload': 0, 'download': 0}
user_traffic[user]['upload'] += conn.get('upload', 0)
user_traffic[user]['download'] += conn.get('download', 0)
# 更新数据库
for username, traffic in user_traffic.items():
self.cursor.execute('''
UPDATE users
SET traffic_used = traffic_used + ?
WHERE username = ?
''', (traffic['upload'] + traffic['download'], username))
# 记录日志
self.cursor.execute('''
INSERT INTO traffic_logs (username, upload, download)
VALUES (?, ?, ?)
''', (username, traffic['upload'], traffic['download']))
self.conn.commit()
def get_user_by_port(self, port):
"""根据端口获取用户"""
self.cursor.execute('SELECT username FROM users WHERE port = ?', (port,))
result = self.cursor.fetchone()
return result[0] if result else None
def check_limits(self):
"""检查用户限制"""
# 检查流量限制
self.cursor.execute('''
SELECT username, traffic_used, traffic_limit
FROM users
WHERE status = 'active' AND traffic_limit > 0
''')
for username, used, limit in self.cursor.fetchall():
if used >= limit:
self.suspend_user(username, '流量超限')
# 检查到期时间
current_time = datetime.now().timestamp()
self.cursor.execute('''
SELECT username
FROM users
WHERE status = 'active' AND expire_date < ?
''', (current_time,))
for (username,) in self.cursor.fetchall():
self.suspend_user(username, '账号过期')
def suspend_user(self, username, reason):
"""暂停用户"""
self.cursor.execute('''
UPDATE users SET status = 'suspended' WHERE username = ?
''', (username,))
self.conn.commit()
print(f"用户 {username} 已暂停:{reason}")
# TODO: 更新 sing-box 配置,移除该用户
self.update_singbox_config()
def update_singbox_config(self):
"""更新 sing-box 配置"""
# 获取所有活跃用户
self.cursor.execute('''
SELECT username, password, port
FROM users
WHERE status = 'active'
''')
users = self.cursor.fetchall()
# 生成新配置
config = self.generate_config(users)
# 保存配置
with open('/etc/sing-box/config.json', 'w') as f:
json.dump(config, f, indent=2)
# 重载服务
import subprocess
subprocess.run(['systemctl', 'reload', 'sing-box'])
def generate_config(self, users):
"""生成 sing-box 配置"""
inbounds = []
for username, password, port in users:
inbounds.append({
"type": "trojan",
"tag": f"{username}-trojan",
"listen": "::",
"listen_port": port,
"users": [
{
"name": username,
"password": password
}
],
"tls": {
"enabled": True,
"server_name": "your-domain.com",
"certificate_path": "/path/to/cert.pem",
"key_path": "/path/to/key.pem"
}
})
return {
"log": {"level": "info"},
"inbounds": inbounds,
"outbounds": [{"type": "direct", "tag": "direct"}]
}
def get_user_stats(self, username):
"""获取用户统计"""
self.cursor.execute('''
SELECT traffic_used, traffic_limit, expire_date, status
FROM users
WHERE username = ?
''', (username,))
result = self.cursor.fetchone()
if result:
used, limit, expire, status = result
return {
"username": username,
"traffic_used": self.format_bytes(used),
"traffic_limit": self.format_bytes(limit) if limit > 0 else "无限制",
"traffic_remaining": self.format_bytes(limit - used) if limit > 0 else "无限制",
"expire_date": datetime.fromtimestamp(expire).strftime('%Y-%m-%d'),
"status": status
}
return None
def format_bytes(self, bytes):
"""格式化字节数"""
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
return f"{bytes:.2f} PB"
def run_monitor(self, interval=60):
"""运行监控"""
print(f"开始监控,每 {interval} 秒更新一次...")
while True:
try:
# 更新流量
self.update_traffic()
# 检查限制
self.check_limits()
# 显示统计
self.show_all_users_stats()
time.sleep(interval)
except KeyboardInterrupt:
print("\n监控已停止")
break
except Exception as e:
print(f"错误: {e}")
time.sleep(interval)
def show_all_users_stats(self):
"""显示所有用户统计"""
print("\n" + "="*60)
print(f"用户流量统计 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("="*60)
self.cursor.execute('SELECT username FROM users')
for (username,) in self.cursor.fetchall():
stats = self.get_user_stats(username)
if stats:
print(f"\n用户: {stats['username']} ({stats['status']})")
print(f" 已用流量: {stats['traffic_used']}")
print(f" 流量限制: {stats['traffic_limit']}")
print(f" 剩余流量: {stats['traffic_remaining']}")
print(f" 到期时间: {stats['expire_date']}")
# 使用示例
if __name__ == "__main__":
monitor = TrafficMonitor()
# 添加用户
monitor.add_user("alice", "alice_password", 8001, traffic_limit_gb=100, expire_days=30)
monitor.add_user("bob", "bob_password", 8002, traffic_limit_gb=50, expire_days=30)
monitor.add_user("charlie", "charlie_password", 8003, traffic_limit_gb=200, expire_days=60)
# 运行监控
monitor.run_monitor(interval=60)用户管理面板
Web 管理界面
创建一个简单的 Web 管理界面:
# web_panel.py - Flask 用户管理面板
from flask import Flask, render_template, request, jsonify, session
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
import hashlib
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(100))
port = db.Column(db.Integer, unique=True)
traffic_limit = db.Column(db.BigInteger, default=0) # 字节
traffic_used = db.Column(db.BigInteger, default=0) # 字节
expire_date = db.Column(db.DateTime)
status = db.Column(db.String(20), default='active')
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_login = db.Column(db.DateTime)
def to_dict(self):
return {
'id': self.id,
'username': self.username,
'email': self.email,
'port': self.port,
'traffic_used': self.format_bytes(self.traffic_used),
'traffic_limit': self.format_bytes(self.traffic_limit),
'traffic_percentage': (self.traffic_used / self.traffic_limit * 100) if self.traffic_limit > 0 else 0,
'expire_date': self.expire_date.strftime('%Y-%m-%d') if self.expire_date else 'Never',
'status': self.status,
'days_remaining': (self.expire_date - datetime.now()).days if self.expire_date else -1
}
@staticmethod
def format_bytes(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
return f"{bytes:.2f} PB"
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/users')
def get_users():
users = User.query.all()
return jsonify([user.to_dict() for user in users])
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.json
# 生成端口号(8001-8999)
existing_ports = [u.port for u in User.query.all()]
port = 8001
while port in existing_ports:
port += 1
# 创建用户
user = User(
username=data['username'],
password=hashlib.sha256(data['password'].encode()).hexdigest(),
email=data.get('email'),
port=port,
traffic_limit=int(data.get('traffic_limit', 100)) * 1024**3, # GB to bytes
expire_date=datetime.now() + timedelta(days=int(data.get('expire_days', 30)))
)
db.session.add(user)
db.session.commit()
# 更新 sing-box 配置
update_singbox_config()
return jsonify({'success': True, 'user': user.to_dict()})
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
user = User.query.get_or_404(user_id)
data = request.json
if 'traffic_limit' in data:
user.traffic_limit = int(data['traffic_limit']) * 1024**3
if 'expire_days' in data:
user.expire_date = datetime.now() + timedelta(days=int(data['expire_days']))
if 'status' in data:
user.status = data['status']
db.session.commit()
update_singbox_config()
return jsonify({'success': True, 'user': user.to_dict()})
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
update_singbox_config()
return jsonify({'success': True})
@app.route('/api/users/<int:user_id>/reset-traffic', methods=['POST'])
def reset_traffic(user_id):
user = User.query.get_or_404(user_id)
user.traffic_used = 0
db.session.commit()
return jsonify({'success': True})
def update_singbox_config():
"""更新 sing-box 配置文件"""
active_users = User.query.filter_by(status='active').all()
# 生成配置...
# 重载 sing-box...
pass
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(host='0.0.0.0', port=5000, debug=True)HTML 模板
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>用户管理面板</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
color: #333;
margin-bottom: 30px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 2em;
font-weight: bold;
color: #667eea;
}
.stat-label {
color: #666;
margin-top: 5px;
}
.users-table {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background: #667eea;
color: white;
padding: 15px;
text-align: left;
}
td {
padding: 15px;
border-bottom: 1px solid #eee;
}
.status-active {
color: #4caf50;
}
.status-suspended {
color: #f44336;
}
.progress-bar {
width: 100%;
height: 10px;
background: #eee;
border-radius: 5px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin: 0 2px;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-danger {
background: #f44336;
color: white;
}
.btn-success {
background: #4caf50;
color: white;
}
.add-user-btn {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
font-size: 30px;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 多用户管理面板</h1>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="total-users">0</div>
<div class="stat-label">总用户数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="active-users">0</div>
<div class="stat-label">活跃用户</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-traffic">0 GB</div>
<div class="stat-label">总流量消耗</div>
</div>
<div class="stat-card">
<div class="stat-value" id="online-users">0</div>
<div class="stat-label">在线用户</div>
</div>
</div>
<div class="users-table">
<table>
<thead>
<tr>
<th>用户名</th>
<th>端口</th>
<th>流量使用</th>
<th>到期时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="users-tbody">
<!-- 用户数据将在这里动态添加 -->
</tbody>
</table>
</div>
<button class="add-user-btn" onclick="showAddUserModal()">+</button>
</div>
<script>
// 加载用户数据
async function loadUsers() {
const response = await fetch('/api/users');
const users = await response.json();
const tbody = document.getElementById('users-tbody');
tbody.innerHTML = '';
let totalUsers = 0;
let activeUsers = 0;
let totalTraffic = 0;
users.forEach(user => {
totalUsers++;
if (user.status === 'active') activeUsers++;
const row = tbody.insertRow();
row.innerHTML = `
<td>${user.username}</td>
<td>${user.port}</td>
<td>
<div>${user.traffic_used} / ${user.traffic_limit}</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${user.traffic_percentage}%"></div>
</div>
</td>
<td>${user.expire_date} (${user.days_remaining} 天)</td>
<td class="status-${user.status}">${user.status}</td>
<td>
<button class="btn btn-primary" onclick="editUser(${user.id})">编辑</button>
<button class="btn btn-success" onclick="resetTraffic(${user.id})">重置流量</button>
<button class="btn btn-danger" onclick="deleteUser(${user.id})">删除</button>
</td>
`;
});
// 更新统计
document.getElementById('total-users').textContent = totalUsers;
document.getElementById('active-users').textContent = activeUsers;
}
// 定时刷新
loadUsers();
setInterval(loadUsers, 5000);
// 用户操作函数
async function resetTraffic(userId) {
if (confirm('确定要重置该用户的流量统计吗?')) {
await fetch(`/api/users/${userId}/reset-traffic`, { method: 'POST' });
loadUsers();
}
}
async function deleteUser(userId) {
if (confirm('确定要删除该用户吗?')) {
await fetch(`/api/users/${userId}`, { method: 'DELETE' });
loadUsers();
}
}
function showAddUserModal() {
// 显示添加用户弹窗
const username = prompt('用户名:');
const password = prompt('密码:');
const traffic_limit = prompt('流量限制(GB):', '100');
const expire_days = prompt('有效天数:', '30');
if (username && password) {
fetch('/api/users', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username,
password,
traffic_limit,
expire_days
})
}).then(() => loadUsers());
}
}
</script>
</body>
</html>自动化管理脚本
批量用户管理脚本
#!/bin/bash
# user-manager.sh - 批量用户管理脚本
CONFIG_FILE="/etc/sing-box/config.json"
USER_DB="/etc/sing-box/users.db"
# 添加用户
add_user() {
local username=$1
local password=$2
local traffic_limit=$3 # GB
local expire_days=$4
local port=$5
# 生成用户配置
echo "$username|$password|$port|$traffic_limit|$expire_days|$(date +%s)" >> $USER_DB
echo "用户 $username 添加成功"
echo "端口: $port"
echo "密码: $password"
echo "流量限制: ${traffic_limit}GB"
echo "有效期: ${expire_days}天"
# 更新配置
update_config
}
# 批量添加用户
batch_add_users() {
local prefix=$1
local count=$2
local traffic_limit=$3
local expire_days=$4
local start_port=8001
for i in $(seq 1 $count); do
username="${prefix}${i}"
password=$(openssl rand -hex 8)
port=$((start_port + i - 1))
add_user "$username" "$password" "$traffic_limit" "$expire_days" "$port"
done
}
# 更新配置文件
update_config() {
# 读取所有用户
local users=()
while IFS='|' read -r username password port traffic_limit expire_days created; do
# 检查是否过期
local current_time=$(date +%s)
local expire_time=$((created + expire_days * 86400))
if [ $current_time -lt $expire_time ]; then
users+=("{\"name\":\"$username\",\"password\":\"$password\",\"port\":$port}")
fi
done < $USER_DB
# 生成新配置
# ... 省略配置生成代码
# 重载服务
systemctl reload sing-box
}
# 导出用户订阅
export_subscription() {
local username=$1
local user_info=$(grep "^$username|" $USER_DB)
if [ -z "$user_info" ]; then
echo "用户不存在"
return 1
fi
IFS='|' read -r name password port _ _ _ <<< "$user_info"
# 生成 Trojan URL
local trojan_url="trojan://${password}@your-domain.com:${port}#${name}"
echo "订阅链接:"
echo "$trojan_url"
# 生成二维码
echo "$trojan_url" | qrencode -t UTF8
}
# 检查用户状态
check_user_status() {
local username=$1
local user_info=$(grep "^$username|" $USER_DB)
if [ -z "$user_info" ]; then
echo "用户不存在"
return 1
fi
IFS='|' read -r name password port traffic_limit expire_days created <<< "$user_info"
# 计算剩余天数
local current_time=$(date +%s)
local expire_time=$((created + expire_days * 86400))
local remaining_days=$(( (expire_time - current_time) / 86400 ))
echo "用户: $name"
echo "端口: $port"
echo "流量限制: ${traffic_limit}GB"
echo "剩余天数: ${remaining_days}天"
# 获取流量使用(从 iptables)
local traffic=$(iptables -nvxL | grep "dpt:$port" | awk '{sum+=$2} END {print sum}')
if [ -n "$traffic" ]; then
local traffic_gb=$(echo "scale=2; $traffic / 1024 / 1024 / 1024" | bc)
echo "已用流量: ${traffic_gb}GB"
fi
}
# 主菜单
show_menu() {
echo "====== 用户管理系统 ======"
echo "1. 添加单个用户"
echo "2. 批量添加用户"
echo "3. 查看用户状态"
echo "4. 导出用户订阅"
echo "5. 删除用户"
echo "6. 重置用户流量"
echo "7. 延长用户期限"
echo "0. 退出"
echo "========================="
}
# 主程序
main() {
while true; do
show_menu
read -p "请选择操作: " choice
case $choice in
1)
read -p "用户名: " username
read -p "密码(留空自动生成): " password
[ -z "$password" ] && password=$(openssl rand -hex 8)
read -p "流量限制(GB): " traffic_limit
read -p "有效天数: " expire_days
read -p "端口: " port
add_user "$username" "$password" "$traffic_limit" "$expire_days" "$port"
;;
2)
read -p "用户名前缀: " prefix
read -p "用户数量: " count
read -p "流量限制(GB): " traffic_limit
read -p "有效天数: " expire_days
batch_add_users "$prefix" "$count" "$traffic_limit" "$expire_days"
;;
3)
read -p "用户名: " username
check_user_status "$username"
;;
4)
read -p "用户名: " username
export_subscription "$username"
;;
0)
echo "退出"
exit 0
;;
*)
echo "无效选择"
;;
esac
echo
read -p "按回车继续..."
done
}
# 初始化
[ ! -f "$USER_DB" ] && touch "$USER_DB"
# 运行主程序
main最佳实践建议
1. 流量限制策略
- 日流量限制:防止突发大流量
- 月流量限制:总体控制
- 速度限制:限制带宽使用
2. 用户分组管理
{
"groups": {
"vip": {
"traffic_limit": "500GB",
"speed_limit": "100Mbps",
"expire_days": 365
},
"premium": {
"traffic_limit": "200GB",
"speed_limit": "50Mbps",
"expire_days": 90
},
"basic": {
"traffic_limit": "50GB",
"speed_limit": "10Mbps",
"expire_days": 30
}
}
}3. 自动化任务
# crontab -e
# 每小时更新流量统计
0 * * * * /usr/local/bin/update_traffic.sh
# 每天凌晨检查过期用户
0 0 * * * /usr/local/bin/check_expired_users.sh
# 每月1号重置流量
0 0 1 * * /usr/local/bin/reset_monthly_traffic.sh4. 监控告警
- 流量超过 80% 时发送提醒
- 账号即将到期提醒
- 异常流量检测
总结
多用户管理的关键点:
- 用户隔离:每个用户独立端口或标签
- 流量统计:准确记录每个用户的使用量
- 限制策略:流量限制、时间限制、速度限制
- 自动化管理:自动检查、自动停用、自动续期
- 友好界面:Web 管理面板,方便操作
- 数据持久化:使用数据库存储用户信息
推荐使用 X-UI 或 3X-UI 等成熟的管理面板,它们已经实现了完整的多用户管理功能。如果需要定制化功能,可以基于本教程的脚本进行开发。
Last updated on