python写的api系统系统用的flask加html加css
没错就是html加flask和css写的前端
开发环境python3.10.9加win11加Redis
Flask==2.3.3
Flask-SQLAlchemy==3.1.1
Flask-Login==0.6.2
redis==5.0.1
urllib3==1.26.16
flask_sqlalchemy==3.1.1
Jinja2==3.1.2
上面是用到的库
你们可以自己配置安装不会安装python的可以用
curl -O https://ghproxy.com/https://raw.githubusercontent.com/lx969788249/lxspacepy/master/pyinstall.sh && chmod +x pyinstall.sh && ./pyinstall.sh
这个是centos系统用的
主要需要root权限
主代码下面是py主代码
import requests
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from datetime import datetime
import secrets
import random
from flask import jsonify
import redis
from flask import Flask, request, jsonify
redis_host = 'localhost' # Redis服务器主机
redis_port = 6379 # Redis服务器端口
redis_db = 0 # Redis数据库编号
cache_timeout = 1 # 缓存超时时间(默认:300秒或5分钟)
redis_conn = redis.StrictRedis(host=redis_host, port=redis_port, db=redis_db)
app = Flask(__name__)
app.config['SECRET_KEY'] = 'jiami'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///D:/sql/test.db'
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# 用户
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(80), nullable=False)
api_key = db.Column(db.String(16), unique=True, nullable=False)
points = db.Column(db.Integer, default=100)
call_logs = db.relationship('DailyCallLog', backref='user', lazy=True)
api_url = db.Column(db.String(255))
api_timeout = db.Column(db.Integer)
filtered_addresses = db.Column(db.String(255))
def generate_api_key(self):
self.api_key = secrets.token_hex(8)
# 每日调用日志
class DailyCallLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date = db.Column(db.Date, nullable=False)
call_count = db.Column(db.Integer, default=0)
# API请求日志
class APILog(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
url = db.Column(db.String(255))
duration = db.Column(db.Float)
client_ip = db.Column(db.String(20))
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/')
def home():
return render_template('sy.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# 输入验证
if len(username) > 20 or len(password) > 20:
flash('用户名和密码不能超过20个字符。', 'danger')
return redirect(url_for('sb_page')) # 在登录失败时重定向到 sb.html
user = User.query.filter_by(username=username).first()
if user and user.password == password:
login_user(user)
flash('登录成功!', 'success')
return redirect(url_for('dashboard'))
else:
flash('登录失败,请检查您的凭据。', 'danger')
return redirect(url_for('sb_page')) # 在登录失败时重定向到 sb.html 页面
return render_template('login.html')
@app.route('/sb_page')
def sb_page():
return render_template('sb.html')
@app.route('/dashboard')
@login_required
def dashboard():
user_points = current_user.points
today = datetime.utcnow().date()
user_id = current_user.id
daily_call_log = DailyCallLog.query.filter_by(user_id=user_id, date=today).first()
if daily_call_log is None:
daily_call_log = DailyCallLog(user_id=user_id, date=today)
db.session.add(daily_call_log)
db.session.commit()
calls_today = daily_call_log.call_count
api_logs = APILog.query.filter_by(user_id=user_id).order_by(APILog.timestamp.desc()).limit(10)
return render_template('info.html', username=current_user.username, user_points=user_points, calls_today=calls_today, api_logs=api_logs)
@app.route('/logout')
@login_required
def logout():
logout_user()
flash('成功登出!', 'success')
return redirect(url_for('login'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if len(username) > 15:
flash('用户名不能超过15个字符。', 'danger')
return redirect(url_for('zcsb')) # 302跳转到zcsb.html页面
existing_user = User.query.filter_by(username=username).first()
if existing_user:
flash('用户名已存在,请选择另一个用户名。', 'danger')
return redirect(url_for('zcsb')) # 302跳转到zcsb.html页面
else:
admin_user = User.query.filter_by(username='admin').first()
if admin_user:
api_url = admin_user.api_url
api_timeout = admin_user.api_timeout
else:
api_url = '默认的API URL'
api_timeout = 10
user_key = secrets.token_hex(8)
new_user = User(username=username, password=password, api_key=user_key, api_url=api_url, api_timeout=api_timeout)
initial_points = 100
new_user.points = initial_points
db.session.add(new_user)
db.session.commit()
flash('注册成功!请登录。', 'success')
return redirect(url_for('login'))
return render_template('register.html')
@app.route('/zcsb')
def zcsb():
# 渲染zcsb.html页面
return render_template('zcsb.html')
@app.route('/admin_login', methods=['GET', 'POST'])
def admin_login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username == 'admin' and password == '123456':
session['admin'] = True
flash('后台登录成功!', 'success')
return redirect(url_for('admin_dashboard'))
else:
flash('后台登录失败,请检查您的凭据。', 'danger')
return render_template('admin_login.html')
@app.route('/admin_dashboard', methods=['GET', 'POST'])
@login_required
def admin_dashboard():
if 'admin' not in session or not session['admin']:
flash('您没有权限访问该页面。', 'danger')
return redirect(url_for('login'))
if request.method == 'POST':
filtered_addresses = request.form.get('filtered_addresses')
current_user.filtered_addresses = filtered_addresses
db.session.commit()
flash('过滤地址已成功更新!', 'success')
return render_template('admin_dashboard.html', username=current_user.username, current_api_url=current_user.api_url, current_api_timeout=current_user.api_timeout, filtered_addresses=current_user.filtered_addresses)
app = Flask(__name__)
app.config['SECRET_KEY'] = 'jiami'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///D:/sql/test.db'
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# 用户
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(80), nullable=False)
api_key = db.Column(db.String(16), unique=True, nullable=False)
points = db.Column(db.Integer, default=100)
call_logs = db.relationship('DailyCallLog', backref='user', lazy=True)
api_url = db.Column(db.String(255))
api_timeout = db.Column(db.Integer)
def generate_api_key(self):
self.api_key = secrets.token_hex(8)
# 每日调用日志
class DailyCallLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date = db.Column(db.Date, nullable=False)
call_count = db.Column(db.Integer, default=0)
# API请求日志
class APILog(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
url = db.Column(db.String(255))
duration = db.Column(db.Float)
client_ip = db.Column(db.String(20))
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/')
def home():
return render_template('sy.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# 输入验证
if len(username) > 20 or len(password) > 20:
flash('用户名和密码不能超过20个字符。', 'danger')
return redirect(url_for('sb_page')) # 在登录失败时重定向到 sb.html
user = User.query.filter_by(username=username).first()
if user and user.password == password:
login_user(user)
flash('登录成功!', 'success')
return redirect(url_for('dashboard'))
else:
flash('登录失败,请检查您的凭据。', 'danger')
return redirect(url_for('sb_page')) # 在登录失败时重定向到 sb.html 页面
return render_template('login.html')
@app.route('/sb_page')
def sb_page():
return render_template('sb.html')
@app.route('/dashboard')
@login_required
def dashboard():
user_points = current_user.points
today = datetime.utcnow().date()
user_id = current_user.id
daily_call_log = DailyCallLog.query.filter_by(user_id=user_id, date=today).first()
if daily_call_log is None:
daily_call_log = DailyCallLog(user_id=user_id, date=today)
db.session.add(daily_call_log)
db.session.commit()
calls_today = daily_call_log.call_count
api_logs = APILog.query.filter_by(user_id=user_id).order_by(APILog.timestamp.desc()).limit(10)
return render_template('info.html', username=current_user.username, user_points=user_points, calls_today=calls_today, api_logs=api_logs)
@app.route('/logout')
@login_required
def logout():
logout_user()
flash('成功登出!', 'success')
return redirect(url_for('login'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if len(username) > 15:
flash('用户名不能超过15个字符。', 'danger')
return redirect(url_for('zcsb')) # 302跳转到zcsb.html页面
existing_user = User.query.filter_by(username=username).first()
if existing_user:
flash('用户名已存在,请选择另一个用户名。', 'danger')
return redirect(url_for('zcsb')) # 302跳转到zcsb.html页面
else:
admin_user = User.query.filter_by(username='admin').first()
if admin_user:
api_url = admin_user.api_url
api_timeout = admin_user.api_timeout
else:
api_url = '默认的API URL'
api_timeout = 10
user_key = secrets.token_hex(8)
new_user = User(username=username, password=password, api_key=user_key, api_url=api_url, api_timeout=api_timeout)
initial_points = 100
new_user.points = initial_points
db.session.add(new_user)
db.session.commit()
flash('注册成功!请登录。', 'success')
return redirect(url_for('login'))
return render_template('register.html')
@app.route('/zcsb')
def zcsb():
# 渲染zcsb.html页面
return render_template('zcsb.html')
@app.route('/admin_login', methods=['GET', 'POST'])
def admin_login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username == 'admin' and password == '123456':
session['admin'] = True
flash('后台登录成功!', 'success')
return redirect(url_for('admin_dashboard'))
else:
flash('后台登录失败,请检查您的凭据。', 'danger')
return render_template('admin_login.html')
@app.route('/admin_dashboard', methods=['GET', 'POST'])
@login_required
def admin_dashboard():
if 'admin' not in session or not session['admin']:
flash('您没有访问该页面的权限。', 'danger')
return redirect(url_for('login'))
if request.method == 'POST':
api_url = request.form.get('api_url')
api_timeout = request.form.get('api_timeout')
User.query.update({'api_url': api_url, 'api_timeout': api_timeout})
db.session.commit()
flash('API设置已保存!', 'success')
return render_template('admin_dashboard.html', username=current_user.username, current_api_url=current_user.api_url, current_api_timeout=current_user.api_timeout)
@app.route('/user_list', methods=['GET'])
@login_required
def user_list():
if 'admin' not in session or not session['admin']:
flash('您没有访问该页面的权限。', 'danger')
return redirect(url_for('login'))
page = request.args.get('page', 1, type=int)
users = User.query.paginate(page=page, per_page=20)
return render_template('user_list.html', users=users)
@app.route('/update_user', methods=['POST'])
@login_required
def update_user():
if 'admin' not in session or not session['admin']:
flash('您没有访问该页面的权限。', 'danger')
return redirect(url_for('login'))
user_id = request.form.get('user_id')
new_password = request.form.get('new_password')
new_api_key = request.form.get('new_api_key')
user = User.query.get(user_id)
if user:
if new_password:
user.password = new_password
if new_api_key:
user.api_key = new_api_key
db.session.commit()
flash('用户信息已更新!', 'success')
else:
flash('无效的用户ID。', 'danger')
return redirect(url_for('user_list'))
@app.route('/update_user_points', methods=['POST'])
@login_required
def update_user_points():
if 'admin' not in session or not session['admin']:
flash('您没有访问该页面的权限。', 'danger')
return redirect(url_for('login'))
user_id = request.form.get('user_id')
new_points = request.form.get('new_points')
user = User.query.get(user_id)
if user:
user.points = int(new_points)
db.session.commit()
flash('用户点数已更新!', 'success')
else:
flash('无效的用户ID。', 'danger')
return redirect(url_for('user_list'))
@app.route('/api', methods=['GET'])
def get_external_url():
api_key = request.args.get('key')
requested_url = request.args.get('url')
user = User.query.filter_by(api_key=api_key).first()
if user:
filtered_addresses = set(user.filtered_addresses.split(',')) if user.filtered_addresses else set()
if requested_url in filtered_addresses:
return jsonify({'code': 405, 'msg': '地址已被过滤'}), 405
try:
if user.points <= 0:
return jsonify({'code': 401, 'diansbuz': '点数不足'}), 402
api_urls = user.api_url.split(',')
random.shuffle(api_urls)
for api_url in api_urls:
full_api_url = api_url + requested_url
api_timeout = user.api_timeout or 10
start_time = datetime.now()
try:
response = requests.get(full_api_url, timeout=api_timeout)
response.raise_for_status()
except requests.exceptions.RequestException as e:
continue
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
api_log = APILog(user_id=user.id, timestamp=end_time, url=requested_url, duration=duration,
client_ip=request.remote_addr)
db.session.add(api_log)
db.session.commit()
if response.status_code == 200:
api_data = response.json()
url = api_data.get('url')
if url:
user.points -= 1
db.session.commit()
today = datetime.utcnow().date()
daily_call_log = DailyCallLog.query.filter_by(user_id=user.id, date=today).first()
if daily_call_log:
daily_call_log.call_count += 1
else:
daily_call_log = DailyCallLog(user_id=user.id, date=today, call_count=1)
db.session.add(daily_call_log)
db.session.commit()
# 成功获取到URL后才设置缓存键
cache_key = f'api_endpoint1:{requested_url}'
redis_conn.setex(cache_key, cache_timeout, url)
return jsonify({'code': 200, 'msg': 'success', 'type': 'm3u8', 'url': url})
else:
return jsonify({'code': 402, 'murl': '未找到URL'})
else:
return jsonify({'code': 403, 'ban': 'API请求失败'})
# 所有API接口请求失败
return jsonify({'code': 404, 'apiban': '所有API接口请求失败'})
except requests.exceptions.RequestException as e:
return jsonify({'code': 500, 'cw': 'API请求出错'})
else:
return jsonify({'code': 401, 'weux': '无效的API密钥'}), 401
@app.route('/change_api_key', methods=['POST'])
@login_required
def change_api_key():
user = current_user
user.generate_api_key()
db.session.commit()
flash('API 密钥已更换!', 'success')
return redirect(url_for('dashboard'))
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
@app.route('/api', methods=['GET'])
def get_external_url():
api_key = request.args.get('key')
requested_url = request.args.get('url')
cache_key = f'api_endpoint1:{requested_url}'
cached_url = redis_conn.get(cache_key)
if cached_url:
return jsonify({'code': 200, 'msg': 'success', 'type': 'm3u8', 'url': cached_url.decode('utf-8')})
user = User.query.filter_by(api_key=api_key).first()
if user:
filtered_addresses = set(user.filtered_addresses.split(',')) if user.filtered_addresses else set()
if requested_url in filtered_addresses:
return jsonify({'code': 405, 'msg': '地址已被过滤'}), 405
try:
if user.points <= 0:
return jsonify({'code': 401, 'diansbuz': '点数不足'}), 402
api_urls = user.api_url.split(',')
random.shuffle(api_urls)
for api_url in api_urls:
full_api_url = api_url + requested_url
api_timeout = user.api_timeout or 10
start_time = datetime.now()
try:
response = requests.get(full_api_url, timeout=api_timeout)
response.raise_for_status()
except requests.exceptions.RequestException as e:
continue
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
api_log = APILog(user_id=user.id, timestamp=end_time, url=requested_url, duration=duration, client_ip=request.remote_addr)
db.session.add(api_log)
db.session.commit()
if response.status_code == 200:
api_data = response.json()
url = api_data.get('url')
if url:
user.points -= 1
db.session.commit()
today = datetime.utcnow().date()
daily_call_log = DailyCallLog.query.filter_by(user_id=user.id, date=today).first()
if daily_call_log:
daily_call_log.call_count += 1
else:
daily_call_log = DailyCallLog(user_id=user.id, date=today, call_count=1)
db.session.add(daily_call_log)
db.session.commit()
# 返回成功的JSON响应
redis_conn.setex(cache_key, cache_timeout, url)
return jsonify({'code': 200, 'msg': 'success', 'type': 'm3u8', 'url': url})
else:
return jsonify({'code': 402, 'murl': '未找到URL'})
else:
return jsonify({'code': 403, 'ban': 'API请求失败'})
# 所有API接口请求失败
return jsonify({'code': 404, 'apiban': '所有API接口请求失败'})
except requests.exceptions.RequestException as e:
return jsonify({'code': 500, 'cw': 'API请求出错'})
else:
return jsonify({'code': 401, 'weux': '无效的API密钥'}), 401
@app.route('/change_api_key', methods=['POST'])
@login_required
def change_api_key():
user = current_user
user.generate_api_key()
db.session.commit()
flash('API 密钥已更换!', 'success')
return redirect(url_for('dashboard'))
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
html代码和css代码我打包在123网盘上https://www.123pan.com/s/xIRRVv-Lg6Qh.html
下面是api系统的部分截图
宝塔pm2来启动监控项目
161行修改后台用户名和登录密码注意这里有一个bug是在前台登录同样的用户名才开源对所有的用户进行api和时间的控制这可能是一个bug后面可能会去修复--