Initial taiko.su easter-egg site
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>单曲活动后台</title>
|
||||
<link rel="icon" href="{{config.assets_baseurl}}img/favicon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="{{config.basedir}}src/css/admin.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="nav">
|
||||
<div class="admin-brand">单曲活动后台</div>
|
||||
<a href="{{config.basedir}}admin/experience" class="active">活动设置</a>
|
||||
<a href="{{config.basedir}}" target="_blank" rel="noopener">打开游玩页</a>
|
||||
<form method="post" action="{{config.basedir}}admin/logout" class="admin-nav-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit">退出登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,132 @@
|
||||
{% extends 'admin.html' %}
|
||||
{% block content %}
|
||||
<div class="admin-page-header">
|
||||
<div>
|
||||
<h1>单曲活动设置</h1>
|
||||
<p class="admin-kicker">公开页面只会加载并游玩这里指定的一首歌曲。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for category, message in get_flashed_messages(with_categories=true) %}
|
||||
<div class="message{% if category == 'error' %} message-error{% endif %}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
|
||||
<form method="post" action="{{ config.basedir }}admin/experience" class="experience-admin-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<section class="admin-panel">
|
||||
<h2>游玩内容</h2>
|
||||
<label class="admin-field">
|
||||
<span>指定歌曲</span>
|
||||
<select name="song_id" id="experience-song">
|
||||
<option value="">暂不开放</option>
|
||||
{% for song in songs %}
|
||||
<option
|
||||
value="{{ song.id }}"
|
||||
{% if settings_doc.song_id == song.id %}selected{% endif %}
|
||||
>#{{ song.id }} · {{ song.title_lang.cn or song.title_lang.en or song.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="admin-field">
|
||||
<span>固定难度</span>
|
||||
<select name="difficulty" id="experience-difficulty" required>
|
||||
{% for difficulty in ['easy', 'normal', 'hard', 'oni', 'ura'] %}
|
||||
<option
|
||||
value="{{ difficulty }}"
|
||||
{% if settings_doc.difficulty == difficulty %}selected{% endif %}
|
||||
>{{ difficulty|upper }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<p class="admin-note">保存时会校验歌曲是否真的包含所选难度。</p>
|
||||
</section>
|
||||
|
||||
<section class="admin-panel">
|
||||
<h2>验证 API Token</h2>
|
||||
<p>
|
||||
当前状态:
|
||||
<strong class="{% if token_configured %}status-ready{% else %}status-missing{% endif %}">
|
||||
{% if token_configured %}已配置{% else %}未配置{% endif %}
|
||||
</strong>
|
||||
</p>
|
||||
<label class="admin-field">
|
||||
<span>设置新 Token</span>
|
||||
<input
|
||||
type="password"
|
||||
name="verification_token"
|
||||
minlength="16"
|
||||
maxlength="512"
|
||||
autocomplete="new-password"
|
||||
placeholder="留空则保留现有 Token"
|
||||
>
|
||||
</label>
|
||||
<label class="admin-check">
|
||||
<input type="checkbox" name="clear_token" value="1">
|
||||
清除当前 Token
|
||||
</label>
|
||||
<p class="admin-note">Token 只保存 SHA-256 摘要,保存后无法从后台读取原文。</p>
|
||||
</section>
|
||||
|
||||
<button type="submit" class="side-button experience-save">保存设置</button>
|
||||
</form>
|
||||
|
||||
<section class="admin-panel code-panel">
|
||||
<div class="admin-page-header">
|
||||
<div>
|
||||
<h2>彩蛋码记录</h2>
|
||||
<p class="admin-kicker">共 {{ code_count }} 个,下面显示最新 {{ codes|length }} 个。</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if codes %}
|
||||
<div class="admin-table-wrap">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>彩蛋码</th>
|
||||
<th>歌曲 / 难度</th>
|
||||
<th>成绩</th>
|
||||
<th>领取时间(UTC)</th>
|
||||
<th>验证次数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in codes %}
|
||||
<tr>
|
||||
<td><code class="egg-code">{{ item.code }}</code></td>
|
||||
<td>
|
||||
{{ item.song_title }}
|
||||
<small>#{{ item.song_id }} · {{ item.difficulty|upper }}</small>
|
||||
</td>
|
||||
<td>
|
||||
{{ item.result.points|default(0) }}
|
||||
<small>良 {{ item.result.good|default(0) }} / 可 {{ item.result.ok|default(0) }} / 不可 {{ item.result.bad|default(0) }}</small>
|
||||
</td>
|
||||
<td>{{ item.created_at.strftime('%Y-%m-%d %H:%M:%S') if item.created_at else '--' }}</td>
|
||||
<td>{{ item.verified_count|default(0) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="admin-message empty-message">还没有用户领取彩蛋码。</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="admin-panel api-panel">
|
||||
<h2>验证接口</h2>
|
||||
<p><code>POST {{ config.basedir }}api/easter-eggs/verify</code></p>
|
||||
<pre>{
|
||||
"headers": {
|
||||
"Authorization": "Bearer <管理员设置的 Token>",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"body": {
|
||||
"code": "AI4-XXXX-XXXX"
|
||||
}
|
||||
}</pre>
|
||||
<p class="admin-note">只有 Token 正确并且彩蛋码存在时,响应中的 <code>success</code> 才会是 <code>true</code>。</p>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>管理员登录</title>
|
||||
<link rel="icon" href="{{ config.assets_baseurl }}img/favicon.png" type="image/png">
|
||||
<link href="{{ config.basedir }}src/css/admin.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="admin-login-page">
|
||||
<main class="admin-login-main">
|
||||
<section class="admin-login-box" aria-labelledby="login-title">
|
||||
<h1 id="login-title">管理员登录</h1>
|
||||
<p class="admin-login-help">登录后可配置唯一游玩曲目、接口令牌并查看彩蛋码。</p>
|
||||
{% for category, message in get_flashed_messages(with_categories=true) %}
|
||||
<div class="message{% if category == 'error' %} message-error{% endif %}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="form-field">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" id="username" name="username" value="{{ username }}" autocomplete="username" required autofocus>
|
||||
<label for="password">密码</label>
|
||||
<input type="password" id="password" name="password" autocomplete="current-password" required>
|
||||
</div>
|
||||
<button type="submit" class="save-song admin-login-button">登录</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{seo.html_lang}}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="{{config.basedir}}">
|
||||
<title>{{seo.title}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
|
||||
<meta name="description" content="{{seo.description}}">
|
||||
<meta name="keywords" content="{{seo.keywords}}">
|
||||
<meta name="application-name" content="taiko.su">
|
||||
<meta name="theme-color" content="#ff5a3d">
|
||||
<meta name="color-scheme" content="only light">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="taiko.su">
|
||||
<meta property="og:title" content="{{seo.title}}">
|
||||
<meta property="og:description" content="{{seo.description}}">
|
||||
<meta property="og:image" content="{{seo.image_url}}">
|
||||
<meta property="og:image:alt" content="{{seo.heading}}">
|
||||
<meta property="og:url" content="{{seo.canonical_url}}">
|
||||
<meta property="og:locale" content="{{seo.og_locale}}">
|
||||
{% for alternate in seo.alternate_urls if alternate.lang != seo.lang %}
|
||||
<meta property="og:locale:alternate" content="{{alternate.og_locale}}">
|
||||
{% endfor %}
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="{{seo.title}}">
|
||||
<meta name="twitter:description" content="{{seo.description}}">
|
||||
<meta name="twitter:image" content="{{seo.image_url}}">
|
||||
<meta name="twitter:image:alt" content="{{seo.heading}}">
|
||||
|
||||
<link rel="canonical" href="{{seo.canonical_url}}">
|
||||
{% for alternate in seo.alternate_urls %}
|
||||
<link rel="alternate" hreflang="{{alternate.hreflang}}" href="{{alternate.url}}">
|
||||
{% endfor %}
|
||||
<link rel="alternate" hreflang="x-default" href="{{seo.default_url}}">
|
||||
<link rel="icon" href="{{config.assets_baseurl}}img/favicon.ico" sizes="any">
|
||||
<link rel="icon" href="{{config.assets_baseurl}}img/favicon-32.png" type="image/png" sizes="32x32">
|
||||
<link rel="icon" href="{{config.assets_baseurl}}img/favicon-16.png" type="image/png" sizes="16x16">
|
||||
<link rel="apple-touch-icon" href="{{config.assets_baseurl}}img/apple-touch-icon.png">
|
||||
<link rel="manifest" href="{{config.basedir}}manifest.json">
|
||||
|
||||
<link rel="stylesheet" href="{{config.basedir}}src/css/loader.css?{{version.commit_short}}">
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "VideoGame",
|
||||
"name": "taiko.su",
|
||||
"alternateName": {{seo.heading|tojson}},
|
||||
"applicationCategory": "Game",
|
||||
"operatingSystem": ["Windows", "macOS", "Linux", "Android", "iOS"],
|
||||
"gamePlatform": ["Web browser", "PC", "Mobile"],
|
||||
"browserRequirements": "JavaScript enabled; modern HTML5 browser",
|
||||
"genre": ["Rhythm", "Music"],
|
||||
"playMode": "SinglePlayer",
|
||||
"isAccessibleForFree": true,
|
||||
"featureList": ["Single-song event"],
|
||||
"inLanguage": {{seo.html_lang|tojson}},
|
||||
"description": {{seo.description|tojson}},
|
||||
"disambiguatingDescription": {{seo.disclaimer|tojson}},
|
||||
"url": {{seo.canonical_url|tojson}},
|
||||
"image": {{seo.image_url|tojson}},
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "USD",
|
||||
"availability": "https://schema.org/InStock"
|
||||
},
|
||||
"potentialAction": {
|
||||
"@type": "PlayAction",
|
||||
"target": {{seo.canonical_url|tojson}}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="{{config.basedir}}src/js/assets.js?{{version.commit_short}}"></script>
|
||||
<script src="{{config.basedir}}src/js/strings-lite.js?{{version.commit_short}}"></script>
|
||||
<script src="{{config.basedir}}src/js/pageevents.js?{{version.commit_short}}"></script>
|
||||
<script src="{{config.basedir}}src/js/loader.js?{{version.commit_short}}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="assets"></div>
|
||||
<div id="screen" class="pattern-bg">
|
||||
<div id="initial-loader" role="status" aria-live="polite">
|
||||
<div class="initial-loader-title">taiko.su</div>
|
||||
<div class="initial-loader-bar"><span></span></div>
|
||||
<div class="initial-loader-status">Loading</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-nosnippet id="version">
|
||||
{% if version.version and version.commit_short and version.commit %}
|
||||
<a href="{{version.url}}commit/{{version.commit}}" target="_blank" rel="noopener" id="version-link"
|
||||
class="stroke-sub" alt="vSU? ??.??.??">vSU? ??.??.??</a>
|
||||
{% else %}
|
||||
<a target="_blank" rel="noopener" id="version-link" class="stroke-sub" alt="vSU? ??.??.??">vSU? ??.??.??</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script src="{{config.basedir}}src/js/browsersupport.js?{{version.commit_short}}"></script>
|
||||
<script src="{{config.basedir}}src/js/kiosk-main.js?{{version.commit_short}}"></script>
|
||||
<noscript>
|
||||
<div data-nosnippet id="unsupportedBrowser">
|
||||
<div id="unsupportedWarn">!</div>
|
||||
<span>taiko.su requires JavaScript to be enabled in your browser.<br>taiko.su 需要启用 JavaScript。<br>taiko.su を遊ぶには JavaScript を有効にしてください。</span>
|
||||
</div>
|
||||
</noscript>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user