2.0.0 Beta : Self-hosted Release
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import hmac
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
current_app,
|
||||
flash,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
|
||||
from web.auth import validate_csrf
|
||||
bp = Blueprint('access', __name__, url_prefix='/access')
|
||||
|
||||
|
||||
@bp.route('/', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if current_app.config['SITE_VISIBILITY'] != 'private':
|
||||
return redirect(url_for('main.index'))
|
||||
if request.method == 'POST':
|
||||
validate_csrf()
|
||||
token = request.form.get('token') or ''
|
||||
viewer_token = current_app.config['VIEWER_TOKEN']
|
||||
if viewer_token and hmac.compare_digest(
|
||||
token,
|
||||
viewer_token,
|
||||
):
|
||||
session['viewer_access'] = True
|
||||
target = request.args.get('next')
|
||||
if not target or not target.startswith('/') or target.startswith('//'):
|
||||
target = url_for('main.index')
|
||||
return redirect(target)
|
||||
flash('访问密码错误', 'error')
|
||||
return render_template('access/login.html')
|
||||
|
||||
|
||||
@bp.route('/logout')
|
||||
def logout():
|
||||
session.pop('viewer_access', None)
|
||||
return redirect(url_for('access.login'))
|
||||
@@ -13,6 +13,9 @@ from web.auth import admin_required, csrf_protected, validate_csrf
|
||||
from web.database import query_db
|
||||
from web.services.admin_service import AdminService
|
||||
from web.services.etl_service import EtlService
|
||||
from web.services.team_context_service import TeamContextService
|
||||
from web.services.web_service import WebService
|
||||
from web.services.roster_version_service import RosterVersionService
|
||||
import hmac
|
||||
import time
|
||||
|
||||
@@ -38,11 +41,47 @@ def logout():
|
||||
@bp.route('/')
|
||||
@admin_required
|
||||
def dashboard():
|
||||
if not TeamContextService.get_active_roster_ids():
|
||||
return redirect(url_for('admin.setup'))
|
||||
return render_template(
|
||||
'admin/dashboard.html',
|
||||
**AdminService.get_overview(),
|
||||
)
|
||||
|
||||
|
||||
@bp.route('/setup', methods=['GET', 'POST'])
|
||||
@admin_required
|
||||
def setup():
|
||||
if request.method == 'POST':
|
||||
validate_csrf()
|
||||
name = (request.form.get('name') or '').strip()
|
||||
description = (request.form.get('description') or '').strip()
|
||||
raw_ids = request.form.get('steam_ids') or ''
|
||||
player_ids = []
|
||||
for value in raw_ids.replace(',', '\n').splitlines():
|
||||
steam_id = value.strip()
|
||||
if steam_id and steam_id not in player_ids:
|
||||
player_ids.append(steam_id)
|
||||
if not name:
|
||||
flash('请输入战队名称', 'error')
|
||||
elif not player_ids:
|
||||
flash('至少添加一名队员 Steam ID', 'error')
|
||||
elif any(not value.isdigit() for value in player_ids):
|
||||
flash('Steam ID 只能包含数字', 'error')
|
||||
else:
|
||||
lineup_id = WebService.save_lineup(
|
||||
name,
|
||||
description,
|
||||
player_ids,
|
||||
)
|
||||
RosterVersionService.snapshot_roster(
|
||||
player_ids,
|
||||
name=f'{name} Initial Roster',
|
||||
)
|
||||
flash('战队初始化完成', 'success')
|
||||
return redirect(url_for('admin.dashboard'))
|
||||
return render_template('admin/setup.html')
|
||||
|
||||
@bp.route('/data-integrity')
|
||||
@admin_required
|
||||
def data_integrity():
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
from flask import Blueprint, render_template, request, jsonify
|
||||
from web.database import query_db
|
||||
from web.config import Config
|
||||
from web.services.stats_service import StatsService
|
||||
|
||||
bp = Blueprint('main', __name__)
|
||||
|
||||
|
||||
@bp.route('/healthz')
|
||||
def healthz():
|
||||
try:
|
||||
for db_name in ('l2', 'l3', 'web'):
|
||||
query_db(db_name, 'SELECT 1', one=True)
|
||||
except Exception as exc:
|
||||
return jsonify({
|
||||
'status': 'unhealthy',
|
||||
'error': str(exc),
|
||||
'brand': Config.BRAND_PRIMARY,
|
||||
}), 503
|
||||
return jsonify({
|
||||
'status': 'ok',
|
||||
'version': '2.0.0-beta',
|
||||
'brand': Config.BRAND_PRIMARY,
|
||||
})
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
recent_matches = StatsService.get_recent_matches(limit=5)
|
||||
|
||||
Reference in New Issue
Block a user