zhuancla / app.py
nbugs's picture
Update app.py
4f5d5ba verified
raw
history blame
5.21 kB
import os
import urllib.parse
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import check_password_hash, generate_password_hash
from dotenv import load_dotenv
from converter import converter # 导入自定义转换器
# 加载环境变量
load_dotenv()
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'starsky_secret_key')
# 登录管理配置
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
# 用户类
class User(UserMixin):
def __init__(self, id, username, password_hash):
self.id = id
self.username = username
self.password_hash = password_hash
# 默认用户
default_username = os.environ.get('ADMIN_USERNAME', 'admin')
default_password = os.environ.get('ADMIN_PASSWORD', 'admin123')
# 用户存储
users = {
default_username: User(
default_username,
default_username,
generate_password_hash(default_password)
)
}
@login_manager.user_loader
def load_user(user_id):
return users.get(user_id)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = users.get(username)
if user and check_password_hash(user.password_hash, password):
login_user(user)
return redirect(url_for('index'))
else:
flash('用户名或密码错误')
return render_template('login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
@app.route('/')
@login_required
def index():
return render_template('index.html')
@app.route('/convert', methods=['POST'])
@login_required
def convert():
# 获取参数
backend_url = request.form.get('backend_url', 'https://raw.githubusercontent.com/yuanwangokk-1/subscribe/refs/heads/main/ACL4SSR/ACL4SSR.ini')
target = request.form.get('target', 'clash')
original_url = request.form.get('original_url', '')
if not original_url:
return jsonify({"status": "error", "message": "订阅链接不能为空"})
# 使用自定义转换器处理(处理整个转换逻辑)
try:
# 使用本地转换器进行转换
result = converter.convert(original_url, target, backend_url)
# 如果转换失败
if result.get('status') == 'error':
return jsonify(result)
# 如果返回的是字符串,说明是配置内容,我们需要将它放入临时文件并提供下载链接
# 但是在HF Space中,我们可以直接通过API返回内容
if isinstance(result.get('result'), str):
# 这里我们直接返回配置文件内容的下载链接
# 实际部署时,我们会添加一个下载路由
response_data = {
"status": "success",
"message": "转换成功",
"result": f"/download?target={target}&token={_generate_download_token(original_url)}"
}
# 保存转换结果到会话中,用于下载
if not hasattr(current_user, 'conversion_results'):
current_user.conversion_results = {}
current_user.conversion_results[_generate_download_token(original_url)] = {
'content': result.get('result'),
'target': target
}
return jsonify(response_data)
# 否则返回结果
return jsonify(result)
except Exception as e:
return jsonify({"status": "error", "message": f"处理失败: {str(e)}"})
def _generate_download_token(url):
"""生成下载令牌,简单实现"""
import hashlib
import time
token = hashlib.md5(f"{url}:{time.time()}".encode()).hexdigest()
return token
@app.route('/download')
@login_required
def download():
target = request.args.get('target', 'clash')
token = request.args.get('token', '')
# 获取保存的转换结果
if not hasattr(current_user, 'conversion_results') or token not in current_user.conversion_results:
return "配置文件不存在或已过期", 404
result = current_user.conversion_results[token]
content = result['content']
# 根据目标类型设置合适的文件名和MIME类型
filename = f"config_{target}.yaml" if target == 'clash' else f"config_{target}.txt"
mimetype = 'application/x-yaml' if target == 'clash' else 'text/plain'
# 返回文件下载
from flask import Response
response = Response(content, mimetype=mimetype)
response.headers['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
# 不要在这里调用 app.run()
# 让 Hugging Face 的 WSGI 服务器来运行应用
# 仅在本地开发时使用以下代码
if __name__ == '__main__' and os.environ.get('DEVELOPMENT') == 'true':
app.run(host='0.0.0.0', port=7860, debug=True)