feat: add ntfy access token support

- ntfy push notifications now include a Bearer access token when configured
- Add Access Token field to /admin/settings next to server and topic
- Token is stored encrypted like the SMTP password
- Bump version to 0.5.7
This commit is contained in:
Troll (Hermes Agent) 2026-08-07 17:21:04 +00:00
parent a56f6e498b
commit 5ba4f28faf
4 changed files with 19 additions and 5 deletions

View file

@ -1,6 +1,6 @@
# Theme Song Booth # Theme Song Booth
**Version:** `v0.5.6` **Version:** `v0.5.7`
A Flask web application for running a convention booth where visitors request a custom AI-generated theme song. Operators manage the queue from an admin dashboard, generate Suno prompts, upload MP3 previews, collect payment, and deliver final songs by email. A Flask web application for running a convention booth where visitors request a custom AI-generated theme song. Operators manage the queue from an admin dashboard, generate Suno prompts, upload MP3 previews, collect payment, and deliver final songs by email.

View file

@ -1 +1 @@
0.5.6 0.5.7

13
app.py
View file

@ -415,11 +415,12 @@ def mask_api_key(key):
def get_ntfy_config(): def get_ntfy_config():
"""Return the effective ntfy server URL and topic from runtime settings.""" """Return the effective ntfy server URL, topic, and access token from runtime settings."""
cfg = load_booth_settings() cfg = load_booth_settings()
return { return {
'server': cfg.get('ntfy_server', ''), 'server': cfg.get('ntfy_server', ''),
'topic': cfg.get('ntfy_topic', ''), 'topic': cfg.get('ntfy_topic', ''),
'token': decrypt_value(cfg.get('ntfy_token', '')) or '',
} }
@ -437,6 +438,9 @@ def send_ntfy(message, title='Theme Song Booth', priority='default', tags='bell'
'Priority': priority, 'Priority': priority,
'Tags': tags, 'Tags': tags,
} }
token = ntfy.get('token', '')
if token:
headers['Authorization'] = f'Bearer {token}'
try: try:
resp = requests.post(url, data=message.encode('utf-8'), headers=headers, timeout=10) resp = requests.post(url, data=message.encode('utf-8'), headers=headers, timeout=10)
return resp.status_code in (200, 202) return resp.status_code in (200, 202)
@ -1672,10 +1676,13 @@ def admin_settings():
return redirect(url_for('admin_settings')) return redirect(url_for('admin_settings'))
elif action == 'save_ntfy': elif action == 'save_ntfy':
# Update ntfy push notification server/topic from the settings form. # Update ntfy push notification server/topic/access token from the settings form.
cfg = load_booth_settings() cfg = load_booth_settings()
cfg['ntfy_server'] = request.form.get('ntfy_server', '').strip().rstrip('/') cfg['ntfy_server'] = request.form.get('ntfy_server', '').strip().rstrip('/')
cfg['ntfy_topic'] = request.form.get('ntfy_topic', '').strip() cfg['ntfy_topic'] = request.form.get('ntfy_topic', '').strip()
new_token = request.form.get('ntfy_token', '').strip()
if new_token:
cfg['ntfy_token'] = encrypt_value(new_token)
save_booth_settings(cfg) save_booth_settings(cfg)
flash('ntfy notification settings saved.', 'success') flash('ntfy notification settings saved.', 'success')
return redirect(url_for('admin_settings')) return redirect(url_for('admin_settings'))
@ -1690,7 +1697,7 @@ def admin_settings():
if ok: if ok:
flash('Test ntfy notification sent.', 'success') flash('Test ntfy notification sent.', 'success')
else: else:
flash('Failed to send ntfy test notification. Check server and topic.', 'error') flash('Failed to send ntfy test notification. Check server, topic, and access token.', 'error')
return redirect(url_for('admin_settings')) return redirect(url_for('admin_settings'))
return render_template( return render_template(

View file

@ -308,6 +308,13 @@
<td>ntfy Topic</td> <td>ntfy Topic</td>
<td><input type="text" id="ntfy_topic" name="ntfy_topic" value="{{ ntfy.get('ntfy_topic', '') }}" placeholder="e.g. Troll-AI-ntfy"></td> <td><input type="text" id="ntfy_topic" name="ntfy_topic" value="{{ ntfy.get('ntfy_topic', '') }}" placeholder="e.g. Troll-AI-ntfy"></td>
</tr> </tr>
<tr>
<td>ntfy Access Token</td>
<td>
<input type="password" id="ntfy_token" name="ntfy_token" placeholder="{% if ntfy.get('ntfy_token') %}Stored encrypted — type to replace{% else %}Enter token (optional){% endif %}">
<p class="copy-hint">Required if the topic is access-controlled. Leave blank to keep the existing stored token.</p>
</td>
</tr>
</table> </table>
<button type="submit">Save ntfy Settings</button> <button type="submit">Save ntfy Settings</button>
</form> </form>