feat(api): add public RESTful API with API key auth and documentation page

This commit is contained in:
2026-07-07 09:22:11 +08:00
parent 14c226d082
commit 6a49fedfbb
9 changed files with 910 additions and 5 deletions

View File

@@ -6,8 +6,9 @@
</div>
<div class="tabs" id="tabs">
<button class="tab {% if active_tab != 'showcase' %}active{% endif %}" onclick="switchTab('account')">{{ 'settings.tab_account'|t(user.language) }}</button>
<button class="tab {% if active_tab != 'showcase' and active_tab != 'apikeys' %}active{% endif %}" onclick="switchTab('account')">{{ 'settings.tab_account'|t(user.language) }}</button>
<button class="tab {% if active_tab == 'showcase' %}active{% endif %}" onclick="switchTab('showcase')">{{ 'settings.tab_showcase'|t(user.language) }}</button>
<button class="tab {% if active_tab == 'apikeys' %}active{% endif %}" onclick="switchTab('apikeys')">{{ 'settings.tab_apikeys'|t(user.language) }}</button>
</div>
{% if success %}
@@ -17,7 +18,7 @@
<div class="alert alert-error">{{ error }}</div>
{% endif %}
<div id="tab-account" class="tab-panel" {% if active_tab == 'showcase' %}style="display:none"{% endif %}>
<div id="tab-account" class="tab-panel" {% if active_tab == 'showcase' or active_tab == 'apikeys' %}style="display:none"{% endif %}>
<div class="form-card">
<h3 style="margin-bottom:.75rem">{{ 'settings.change_username'|t(user.language) }}</h3>
<form method="post" action="/settings/change-username">
@@ -155,6 +156,61 @@
</div>
</div>
<div id="tab-apikeys" class="tab-panel" {% if active_tab != 'apikeys' %}style="display:none"{% endif %}>
<div class="form-card">
<h3 style="margin-bottom:.75rem">{{ 'apikey.title'|t(user.language) }}</h3>
<p style="color:var(--text-muted);font-size:.85rem;margin-bottom:1rem">{{ 'apikey.hint'|t(user.language) }}</p>
{% if new_key %}
<div class="alert alert-success" style="margin-bottom:1rem">
<strong>{{ 'apikey.created'|t(user.language) }}</strong><br>
<code style="word-break:break-all;font-size:.85rem">{{ new_key }}</code><br>
<small style="color:var(--text-muted)">{{ 'apikey.copy_now'|t(user.language) }}</small>
</div>
{% endif %}
<form method="post" action="/settings/apikeys/create" style="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem">
<input type="text" name="name" placeholder="{{ 'apikey.name_ph'|t(user.language) }}" style="max-width:200px">
<button type="submit" class="btn btn-primary btn-sm">{{ 'apikey.create'|t(user.language) }}</button>
</form>
{% if api_keys %}
<div class="table-wrap">
<table class="table">
<thead>
<tr>
<th>{{ 'apikey.name'|t(user.language) }}</th>
<th>{{ 'apikey.key'|t(user.language) }}</th>
<th>{{ 'apikey.created_at'|t(user.language) }}</th>
<th>{{ 'apikey.last_used'|t(user.language) }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for ak in api_keys %}
<tr>
<td>{{ ak.name or '—' }}</td>
<td><code style="font-size:.8rem">{{ ak.key[:8] }}****</code></td>
<td>{{ ak.created_at|to_local|strftime('%Y-%m-%d') }}</td>
<td>{% if ak.last_used_at %}{{ ak.last_used_at|to_local|strftime('%Y-%m-%d') }}{% else %}—{% endif %}</td>
<td>
<form method="post" action="/settings/apikeys/{{ ak.id }}/delete" style="display:inline" onsubmit="return confirm('{{ 'apikey.confirm_delete'|t(user.language) }}')">
<button type="submit" class="btn btn-danger btn-sm">{{ 'apikey.delete'|t(user.language) }}</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<p>{{ 'apikey.empty'|t(user.language) }}</p>
</div>
{% endif %}
</div>
</div>
<script>
function switchTab(name) {
document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); });