LIM configured no logging at all, so the instruction fetcher had no trail to follow. Adds a rotating file handler on the root logger plus two owner-only admin pages. - Admin > Logs: filter by level, source and text; tracebacks kept with their error; auto-refresh, raw download, rotated-file browsing and rotate-now - Admin > Logs > Brickset API calls: every outbound call recorded with method, params, caller, duration, result count and error, plus daily quota accounting (only getSets counts) and a comparison against getKeyUsageStats - New owner concept (services/ownership.py): a single account, distinct from is_admin, since these pages expose other admins activity. Defaults to the first admin; flask owner show/set to change - Services now log through module loggers; retention setting and pruning for call records - New CLI: flask owner show/set, flask logs path/prune - New table brickset_api_calls, created automatically at boot; LOG_* env vars, /data/logs in Docker
188 lines
8.9 KiB
HTML
188 lines
8.9 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>
|
|
{% if current_user_is_owner %}
|
|
<a href="{{ url_for('logs.index') }}" class="btn btn-outline-dark me-2">
|
|
<i class="bi bi-journal-text"></i> Logs
|
|
</a>
|
|
{% endif %}
|
|
<a href="{{ url_for('backup.index') }}" class="btn btn-outline-danger me-2">
|
|
<i class="bi bi-archive"></i> Backup & 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>
|
|
— 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>
|
|
— 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>
|
|
— 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 %}
|