Guides image + draft gating, audit retention, ADR-015 gate, backup credential redaction

This commit is contained in:
2026-08-26 15:08:53 -04:00
parent 4e38303326
commit 0738d71525
37 changed files with 1953 additions and 129 deletions

View File

@@ -1835,3 +1835,75 @@ def catalog_rename(table, source, target, apply_changes):
{'t': target, 'k': sourcerow[0]})
click.echo(click.style(f'Renamed {source!r} to {target!r}.', fg='green'))
@click.group('audit')
def audit_cli():
"""Audit log maintenance."""
pass
@audit_cli.command('stats')
@with_appcontext
def audit_stats():
"""Show how big the audit log is and what retention is configured."""
from shopdb.core.services import auditretention
days = auditretention.configureddays()
info = auditretention.stats(days or None)
click.echo(f"Entries: {info['total']:,}")
click.echo(f"Oldest: {info['oldest'] or '-'}")
click.echo(f"Newest: {info['newest'] or '-'}")
if days:
click.echo(f"Retention: {days} days")
click.echo(f"Expired: {info['expired']:,} entry(s) older than that")
else:
click.echo('Retention: not set - nothing is ever deleted')
click.echo("Set one in Settings > Site & Facility, or pass --days here.")
@audit_cli.command('purge')
@click.option('--days', type=int, default=None,
help='Override the configured retention for this run.')
@click.option('--apply', 'apply_changes', is_flag=True,
help='Actually delete. Without this the command only reports.')
@with_appcontext
def audit_purge(days, apply_changes):
"""Delete audit entries older than the retention window.
Reports by default and deletes only with --apply, because this is the one
command in the app that destroys history a site may be keeping deliberately.
"""
from shopdb.core.services import auditretention
days = days if days is not None else auditretention.configureddays()
if not days:
click.echo(click.style(
'No retention configured, so there is nothing to purge. Set '
'audit_retentiondays in Settings > Site & Facility, or pass --days.',
fg='yellow'))
return
preview = auditretention.purge(days, dryrun=True)
click.echo(f"Retention {days} days - cutoff {preview['cutoff']}")
click.echo(f"{preview['matched']:,} entry(s) are older than that.")
if not preview['matched']:
return
if not apply_changes:
click.echo(click.style(
'Dry run. Nothing was deleted. Re-run with --apply to write.',
fg='yellow'))
return
# No batch cap here: unlike the API this is not behind a proxy timeout, and
# a purge that stops halfway leaves the operator wondering which half.
result = auditretention.purge(days)
click.echo(click.style(
f"Deleted {result['deleted']:,} entry(s) in {result['batches']} batch(es).",
fg='green'))
if not result['complete']:
click.echo(click.style(
f"{result['remaining']:,} still older than the cutoff - re-run.",
fg='yellow'))