generated from dellevin/template
feat: complete local version to overwrite remote
This commit is contained in:
287
www_comments/app.py
Normal file
287
www_comments/app.py
Normal file
@@ -0,0 +1,287 @@
|
||||
from flask import Flask, request, jsonify, render_template, session, redirect, url_for
|
||||
from flask_cors import CORS
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
from config import *
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app) # 允许跨域请求
|
||||
app.secret_key = SECRET_KEY
|
||||
|
||||
# 数据文件路径已在 config.py 中定义
|
||||
# DATA_FILE = os.path.join(os.path.dirname(__file__), 'data', 'comments.json')
|
||||
|
||||
def read_comments():
|
||||
"""读取评论数据"""
|
||||
try:
|
||||
if os.path.exists(DATA_FILE):
|
||||
with open(DATA_FILE, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
return {"comments": []}
|
||||
except Exception as e:
|
||||
print(f"读取数据失败: {e}")
|
||||
return {"comments": []}
|
||||
|
||||
def save_comments(data):
|
||||
"""保存评论数据"""
|
||||
os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
|
||||
with open(DATA_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def parse_user_agent(ua):
|
||||
"""解析 User Agent 获取浏览器和系统信息"""
|
||||
browser = 'Unknown'
|
||||
os_name = 'Unknown'
|
||||
|
||||
# 浏览器检测
|
||||
if 'Chrome' in ua and 'Edge' not in ua:
|
||||
browser = 'Chrome'
|
||||
elif 'Firefox' in ua:
|
||||
browser = 'Firefox'
|
||||
elif 'Safari' in ua and 'Chrome' not in ua:
|
||||
browser = 'Safari'
|
||||
elif 'Edge' in ua:
|
||||
browser = 'Edge'
|
||||
|
||||
# 系统检测
|
||||
if 'Windows' in ua:
|
||||
os_name = 'Windows'
|
||||
elif 'Mac' in ua:
|
||||
os_name = 'macOS'
|
||||
elif 'Linux' in ua:
|
||||
os_name = 'Linux'
|
||||
elif 'Android' in ua:
|
||||
os_name = 'Android'
|
||||
elif 'iPhone' in ua or 'iPad' in ua:
|
||||
os_name = 'iOS'
|
||||
|
||||
return browser, os_name
|
||||
|
||||
@app.route('/api/comment', methods=['GET'])
|
||||
def get_comments():
|
||||
"""获取评论列表(前端展示用,仅返回已审核)"""
|
||||
try:
|
||||
page = int(request.args.get('page', 1))
|
||||
page_size = int(request.args.get('pageSize', PAGE_SIZE_FRONTEND))
|
||||
|
||||
data = read_comments()
|
||||
|
||||
# 过滤已审核的评论
|
||||
filtered = [c for c in data['comments']
|
||||
if c.get('status') == 'approved']
|
||||
|
||||
# 按时间倒序排序
|
||||
filtered.sort(key=lambda x: x.get('time', 0), reverse=True)
|
||||
|
||||
total_count = len(filtered)
|
||||
total_pages = (total_count + page_size - 1) // page_size
|
||||
|
||||
# 分页
|
||||
start = (page - 1) * page_size
|
||||
end = start + page_size
|
||||
paged_comments = filtered[start:end]
|
||||
|
||||
return jsonify({
|
||||
'errno': 0,
|
||||
'errmsg': '',
|
||||
'data': {
|
||||
'page': page,
|
||||
'totalPages': total_pages,
|
||||
'pageSize': page_size,
|
||||
'count': total_count,
|
||||
'data': paged_comments
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'errno': 1,
|
||||
'errmsg': str(e)
|
||||
}), 500
|
||||
|
||||
# 管理员账号配置已在 config.py 中定义
|
||||
# ADMIN_USERNAME = 'admin'
|
||||
# ADMIN_PASSWORD = 'zhaoyang0902'
|
||||
|
||||
def login_required(f):
|
||||
"""登录验证装饰器"""
|
||||
from functools import wraps
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not session.get('logged_in'):
|
||||
return redirect(url_for('login_page'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
@app.route('/login')
|
||||
def login_page():
|
||||
"""登录页面"""
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/api/login', methods=['POST'])
|
||||
def do_login():
|
||||
"""处理登录请求"""
|
||||
data = request.get_json()
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
|
||||
if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
|
||||
session['logged_in'] = True
|
||||
return jsonify({'errno': 0, 'errmsg': '登录成功'})
|
||||
else:
|
||||
return jsonify({'errno': 1, 'errmsg': '用户名或密码错误'}), 401
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
"""退出登录"""
|
||||
session.pop('logged_in', None)
|
||||
return redirect(url_for('login_page'))
|
||||
|
||||
@app.route('/admin')
|
||||
@login_required
|
||||
def admin_page():
|
||||
"""后台管理页面"""
|
||||
return render_template('admin.html')
|
||||
|
||||
@app.route('/api/admin/comments', methods=['GET'])
|
||||
@login_required
|
||||
def get_admin_comments():
|
||||
"""获取所有评论(后台管理用)"""
|
||||
try:
|
||||
status_filter = request.args.get('status', '')
|
||||
keyword = request.args.get('keyword', '').lower()
|
||||
page = int(request.args.get('page', 1))
|
||||
page_size = int(request.args.get('pageSize', PAGE_SIZE_ADMIN))
|
||||
|
||||
data = read_comments()
|
||||
comments = data['comments']
|
||||
|
||||
# 状态过滤
|
||||
if status_filter:
|
||||
comments = [c for c in comments if c.get('status') == status_filter]
|
||||
else:
|
||||
# 全部状态下默认不显示已删除的评论
|
||||
comments = [c for c in comments if c.get('status') != 'deleted']
|
||||
|
||||
# 关键词搜索(昵称或内容)
|
||||
if keyword:
|
||||
comments = [c for c in comments if
|
||||
keyword in c.get('nick', '').lower() or
|
||||
keyword in c.get('comment', '').lower()]
|
||||
|
||||
# 按时间倒序排序
|
||||
comments.sort(key=lambda x: x.get('time', 0), reverse=True)
|
||||
|
||||
total_count = len(comments)
|
||||
total_pages = (total_count + page_size - 1) // page_size if total_count > 0 else 0
|
||||
|
||||
# 分页
|
||||
start = (page - 1) * page_size
|
||||
end = start + page_size
|
||||
paged_comments = comments[start:end]
|
||||
|
||||
return jsonify({
|
||||
'errno': 0,
|
||||
'data': {
|
||||
'list': paged_comments,
|
||||
'total': total_count,
|
||||
'page': page,
|
||||
'pageSize': page_size,
|
||||
'totalPages': total_pages
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'errno': 1,
|
||||
'errmsg': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/admin/comment/<int:comment_id>', methods=['PUT'])
|
||||
@login_required
|
||||
def update_comment_status(comment_id):
|
||||
"""更新评论状态"""
|
||||
try:
|
||||
update_data = request.get_json()
|
||||
new_status = update_data.get('status')
|
||||
|
||||
if new_status not in ['approved', 'spam', 'deleted']:
|
||||
return jsonify({'errno': 1, 'errmsg': '无效的状态'}), 400
|
||||
|
||||
data = read_comments()
|
||||
found = False
|
||||
for c in data['comments']:
|
||||
if c['id'] == comment_id:
|
||||
c['status'] = new_status
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
return jsonify({'errno': 1, 'errmsg': '评论不存在'}), 404
|
||||
|
||||
save_comments(data)
|
||||
return jsonify({'errno': 0, 'errmsg': '更新成功'})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'errno': 1,
|
||||
'errmsg': str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/api/comment', methods=['POST'])
|
||||
def add_comment():
|
||||
"""添加评论"""
|
||||
try:
|
||||
comment_data = request.get_json()
|
||||
|
||||
if not comment_data:
|
||||
return jsonify({
|
||||
'errno': 1,
|
||||
'errmsg': '无效的数据'
|
||||
}), 400
|
||||
|
||||
data = read_comments()
|
||||
|
||||
# 生成新 ID
|
||||
new_id = max([c.get('id', 0) for c in data['comments']], default=0) + 1
|
||||
|
||||
# 解析 User Agent
|
||||
ua = comment_data.get('ua', '')
|
||||
browser, os_name = parse_user_agent(ua)
|
||||
|
||||
# 生成头像(基于邮箱 MD5)
|
||||
email = comment_data.get('mail', '')
|
||||
avatar_hash = hashlib.md5(email.encode('utf-8')).hexdigest()
|
||||
avatar = f'https://seccdn.libravatar.org/avatar/{avatar_hash}'
|
||||
|
||||
# 创建新评论
|
||||
new_comment = {
|
||||
'id': new_id,
|
||||
'nick': comment_data.get('nick', '匿名'),
|
||||
'mail': comment_data.get('mail', ''),
|
||||
'link': comment_data.get('link', ''),
|
||||
'comment': f"<p>{comment_data.get('comment', '')}</p>\n",
|
||||
'ua': ua,
|
||||
'browser': browser,
|
||||
'os': os_name,
|
||||
'avatar': avatar,
|
||||
'time': int(datetime.now().timestamp() * 1000),
|
||||
'status': 'approved' # 直接审核通过
|
||||
}
|
||||
|
||||
# 添加到数据中
|
||||
data['comments'].append(new_comment)
|
||||
save_comments(data)
|
||||
|
||||
return jsonify({
|
||||
'errno': 0,
|
||||
'errmsg': '',
|
||||
'data': new_comment
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'errno': 1,
|
||||
'errmsg': str(e)
|
||||
}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host=HOST, port=PORT, debug=DEBUG)
|
||||
Reference in New Issue
Block a user