Skip to content

v1.12.1#758

Merged
ajslater merged 280 commits into
mainfrom
develop
May 11, 2026
Merged

v1.12.1#758
ajslater merged 280 commits into
mainfrom
develop

Conversation

@ajslater
Copy link
Copy Markdown
Owner

  • Features
    • Fail2Ban support with a dedicated log.
      • See README and codex.toml.default
  • Fixes
    • Comic filename parsing improvements.

ajslater and others added 29 commits May 9, 2026 11:13
…cker (#751)

Previously the column picker always appended newly-toggled columns to
the end of the order list. This adds canonical-rank-aware insertion: if
the existing draft is in strictly-increasing canonical order (the order
implicit in `_CATEGORIES`), splice the new column into the unique slot
that keeps the sequence sorted. If the user has manually rearranged the
draft out of canonical order, fall back to appending so we don't reshuffle
their layout.

Examples (against the default "p" group `[cover, name, child_count]`):
  toggle favorite  → [cover, favorite, name, child_count]
  toggle imprint   → [cover, imprint_name, name, child_count]
  toggle publisher → [cover, publisher_name, name, child_count]

Covered by 16 new pure-function vitest cases.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BROWSER_CHOICES is dumped to both browser-choices.json (Vuetify list)
and browser-map.json (raw map), but each format is consumed
selectively by the frontend. Add per-file include-key frozensets in
codex/choices/browser.py and look them up from choices_to_json.py so
the generator skips keys the frontend never reads.

Removed orphans:
- browser-choices.json: IDENTIFIER_SOURCES (frontend uses map form)
- browser-map.json: BOOKMARK_FILTER, VUETIFY_NULL_CODE, SETTINGS_GROUP
  (frontend uses Vuetify form)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `.*` glob in `[tool.codespell].skip` matched every walked path
because `os.walk('.')` prefixes paths with `./`, so codespell scanned
nothing during `make lint` while nvim per-file checks still flagged
typos. Replace `.*` with explicit hidden-dir paths, fix the
`/uv.lock` typo, and add legitimate technical terms (wan, crate, iff,
ser, etc.) to ignore-words-list.

Also wire codespell into the frontend lint script so frontend code
gets spell-checked too, and fix one real typo uncovered (secifies →
specifies in opds/v2/progression.py).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* audit and fix tool ignore/skip configs

- fix eslint **/*min.css and **/*min.js globs that over-matched (e.g. admin.css)
- fix [tool.vulture] test_results/ -> test-results/ typo
- move stray builtin = "clear,code,rare" into [tool.codespell]
- fix uv build-backend source-include typos (mkdocks.yml, .circlci/**) and drop dead entries (ci/**, top-level strange.jpg)
- remove duplicate "site" in [tool.basedpyright] exclude
- drop stale codex/_vendor references from all tool configs
- simplify [tool.complexipy] exclude (paths gating made entries unreachable)
- drop redundant entries from [tool.ruff] (dist already in defaults) and [tool.djlint] (covered by use_gitignore = true)
- normalize codespell skip prefixes; replace bare "coverage" with htmlcov + .coverage*; add *.svg, frontend/src/choices
- add coverage, htmlcov, .eslintcache to ESLint base ignores
- add comics/* and vulture_ignorelist.py to radon exclude
- normalize vulture *​/X* patterns to **/X so root-level matches work; align ty exclude with basedpyright
- delete unused [tool.typos] block

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* update deps

* fix remark for claude

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* bump version to alpha

* bump news for favorites
* Add optional failed-login log for fail2ban et al.

Adds a default-off feature that appends one parseable line per failed
credential attempt to a dedicated file, so banning tools (fail2ban,
CrowdSec, sshguard) can tail it via regex. Gated end-to-end on
`auth.failed_login_log` so disabled deployments pay zero overhead.

Hooks `django.contrib.auth.signals.user_login_failed`, which fires for
both rest_registration's form login and DRF's BasicAuthentication
(OPDS), covering both vectors with one receiver. rest_registration's
default authenticator drops `request=`, so a small contextvar
middleware stashes the active request to recover the client IP for
form logins. XFF trust is configurable for direct-exposed deployments
that can't trust the header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Document failed-login log in README

Add env-var references in the Authentication subsection plus a new
"Failed-Login Log" section covering line format, XFF trust trade-off,
and a worked fail2ban filter + jail example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A fresh browser visit to Codex probes /api/v3/auth/profile/ before any
session cookie exists, which Django's BaseHandler.get_response logs as
a WARNING ("Forbidden: /api/v3/auth/profile/") for every first-time
visitor. That drowns the main log in routine noise.

Add a logging.Filter on django.request that downgrades records matching
that exact "Forbidden: <path>" pattern to DEBUG, parameterized by a
small frozenset of known-noisy paths so abuse on other endpoints still
surfaces at WARNING.

Also move django.request into the loggers dict where dictConfig will
actually see it — the old top-level placement was silently ignored.
Records still flow to the root loguru handler via propagation.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add not_failed_login_filter and attach it to the stdout / codex.log
sinks so the IP-bearing line is written only to failed_logins.log.
Django's own request logger still emits a bare WARNING for each
failed attempt ("Unauthorized: /api/v3/auth/login/" etc.) so the
failure remains visible in codex.log without exposing the client IP.

Concentrating IP + username in one file makes the privacy story
easier to reason about: one file to chmod, forward to a SIEM, or
retain on its own schedule.

The inverse filter is only attached when the feature is enabled —
when disabled, no records carry the tag and the filter would be a
no-op anyway.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ajslater ajslater merged commit 3a94862 into main May 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant