Files
lego-instructions-manager/app/templates/admin/settings.html
T
jessikitty 89f65e2d74 Add automatic instruction fetcher for official sets
Downloads official instruction PDFs from LEGO, located via the Brickset API, one set per interval (10 minutes by default) so neither API gets hammered. Official sets only; MOCs are excluded throughout.

- New admin page at /admin/instruction-fetch: queue builder, pacing controls, live countdown and per-set job history

- Database-backed queue (instruction_fetch_jobs) and last-run timestamp, so pacing survives restarts

- Background worker thread, plus 'flask instructions queue/run/status' for Task Scheduler or cron

- SHA-256 dedupe (instructions.sha256), PDF header check, size cap and streaming downloads via temp files

- brickset_api: getInstructions2 by set number and getInstructions by setID (the old code sent a set number to the setID method); getSets params now proper JSON; getKeyUsageStats added

- Schema top-ups applied automatically at boot: new table plus instructions.sha256/source

- Docker: fetcher env vars passed through compose, no worker thread in the entrypoint bootstrap
2026-09-26 13:43:17 +10:00

183 lines
8.7 KiB
HTML

{% extends "base.html" %}
{% block title %}Site Settings - Admin{% endblock %}
{% block content %}
<div class="row mb-4">
<div class="col">
<h1><i class="bi bi-sliders"></i> Site Settings</h1>
</div>
<div class="col-auto">
<a href="{{ url_for('instruction_fetch.index') }}" class="btn btn-outline-warning me-2">
<i class="bi bi-cloud-arrow-down"></i> Instruction Fetcher
</a>
<a href="{{ url_for('backup.index') }}" class="btn btn-outline-danger me-2">
<i class="bi bi-archive"></i> Backup &amp; Restore
</a>
<a href="{{ url_for('admin.dashboard') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back
</a>
</div>
</div>
<div class="row">
<div class="col-lg-7">
<form method="POST">
<input type="hidden" name="action" value="save">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-person-plus"></i> Accounts</h5>
</div>
<div class="card-body">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="allow_registration" name="allow_registration"
{% if settings.allow_registration in ['true', '1', 'yes', 'on'] %}checked{% endif %}>
<label class="form-check-label" for="allow_registration">
<strong>Allow self-registration</strong>
</label>
</div>
<div class="form-text">
When off, the Register page is hidden and blocked, and you create
accounts yourself from
<a href="{{ url_for('admin.users') }}">User Management</a>.
The very first account on a fresh install is always allowed,
so this can't lock you out.
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-eye"></i> Set visibility</h5>
</div>
<div class="card-body">
<label class="form-label"><strong>Default for newly added sets</strong></label>
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="default_visibility"
id="vis_private" value="private"
{% if settings.default_visibility == 'private' %}checked{% endif %}>
<label class="form-check-label" for="vis_private">
<i class="bi bi-lock-fill"></i> <strong>Private</strong>
&mdash; only the owner and administrators.
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="default_visibility"
id="vis_shared" value="shared"
{% if settings.default_visibility == 'shared' %}checked{% endif %}>
<label class="form-check-label" for="vis_shared">
<i class="bi bi-people-fill"></i> <strong>Shared</strong>
&mdash; the owner picks which users can see each set.
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="radio" name="default_visibility"
id="vis_public" value="public"
{% if settings.default_visibility == 'public' %}checked{% endif %}>
<label class="form-check-label" for="vis_public">
<i class="bi bi-globe"></i> <strong>Public</strong>
&mdash; every logged-in user.
</label>
</div>
<div class="alert alert-light border small mb-0">
Visibility only controls who can <em>see</em> a set and its files.
Only the owner and administrators can ever change one.
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-signpost-split"></i> Navigation</h5>
</div>
<div class="card-body">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="home_redirect_dashboard" name="home_redirect_dashboard"
{% if settings.home_redirect_dashboard in ['true', '1', 'yes', 'on'] %}checked{% endif %}>
<label class="form-check-label" for="home_redirect_dashboard">
<strong>Send logged-in users straight to their dashboard</strong>
</label>
</div>
<div class="form-text">
Skips the welcome page for anyone already signed in. Logged-out
visitors still see it.
</div>
</div>
</div>
<button type="submit" class="btn btn-danger">
<i class="bi bi-check-lg"></i> Save settings
</button>
</form>
</div>
<div class="col-lg-5">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">System Information</h5>
</div>
<div class="card-body">
<table class="table table-sm mb-0">
<tr><td>Total Users:</td><td><strong>{{ stats.total_users }}</strong></td></tr>
<tr><td>Total Sets:</td><td><strong>{{ stats.total_sets }}</strong></td></tr>
<tr><td>Total Instructions:</td><td><strong>{{ stats.total_instructions }}</strong></td></tr>
<tr><td>Storage Used:</td>
<td><strong>{{ (stats.total_storage / 1024 / 1024) | round(2) }} MB</strong></td></tr>
</table>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-collection"></i> Existing sets</h5>
</div>
<div class="card-body">
<table class="table table-sm">
<tr>
<td><i class="bi bi-lock-fill"></i> Private</td>
<td class="text-end"><strong>{{ visibility_counts.get('private', 0) }}</strong></td>
</tr>
<tr>
<td><i class="bi bi-people-fill"></i> Shared</td>
<td class="text-end"><strong>{{ visibility_counts.get('shared', 0) }}</strong></td>
</tr>
<tr>
<td><i class="bi bi-globe"></i> Public</td>
<td class="text-end"><strong>{{ visibility_counts.get('public', 0) }}</strong></td>
</tr>
</table>
<p class="small text-muted">
Sets that existed before visibility was added were made
<strong>public</strong>, so nothing disappeared from anyone's library.
Change them all at once here, then adjust individual sets from their
own pages.
</p>
<form method="POST"
onsubmit="return confirm('Change the visibility of EVERY set? This cannot be undone.');">
<input type="hidden" name="action" value="apply_visibility">
<div class="input-group">
<select name="bulk_visibility" class="form-select">
{% for v in visibilities %}
<option value="{{ v }}">{{ v|capitalize }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-outline-danger">
Apply to all
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<p class="text-center text-muted small">Created by: Jess Rogerson (yelling commands at Claude.AI)</p>
{% endblock %}