Skip to content

Clean codebase : Refonte complète du code - #55

Open
raynaldlao wants to merge 47 commits into
masterfrom
clean-codebase
Open

Clean codebase : Refonte complète du code#55
raynaldlao wants to merge 47 commits into
masterfrom
clean-codebase

Conversation

@raynaldlao

@raynaldlao raynaldlao commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Refonte majeure du code : architecture hexagonale nettoyée, couverture de tests passée de 92 % à 99 %, 708 tests, zéro erreur pyright, sécurité renforcée (CSP, rate limiting, CSRF, headers) et dette technique réduite (code mort, duplications, god classes).


Par catégorie

🔬 Qualité du code (24 commits)

  • Code mort supprimé : get_all_ordered_by_date_desc, get_comments_for_article, update_avatar (port), classes CommentEmptyError/CommentTooLongError, CSS .comment-edited/.detail-body
  • Duplication éliminée : _get_account_and_comment() et _get_account_and_article() mutualisés, _ensure_blocknote_format() extrait, _sanitize_comment_content() unifié, décorateurs @require_auth/@require_auth_api centralisés
  • God class réduite : LoginService passe de 436 à 266 lignes (−39%), administration extraite dans AdminService
  • Typage renforcé : NamedTuples remplacent 3 dictionnaires internes, AccountRole enum partout, except Exceptionexcept ValidationError
  • Validation déplacée : UpdatePasswordRequest DTO, FileUploadRequest contraintes
  • Over-engineering évité : validation password non extraite (YAGNI), CSS vendor différé (pattern media="print"), indexes DB V19

🧪 Tests (couverture 92 % → 99 %)

  • 708 tests (pytest) + 123 vitest, 0 erreur pyright
  • Nouvelles suites : adaptateurs, services, DTOs, helpers, SQLAlchemy, in-memory
  • Tests intégration rate limiting, migration V19 indexes, argon2 params

🛡️ Sécurité

  • Rate limiting : flask-limiter, 5 tentatives/min sur /login, callback + flash FR
  • Session unique : token aléatoire en DB + cookie, mismatch → déconnexion silencieuse
  • Headers : CSP, X-Frame-Options, Referrer-Policy, Cache-Control, X-Content-Type-Options
  • CSRF : CSRFProtect activé, tokens dans tous les formulaires POST
  • Exceptions : IntegrityError catché → pas de fuite de nom de contrainte SQL
  • Dépendances : flask-login et flask-uploads retirées

♿ Accessibilité (18 issues ARIA résolues)

  • aria-current="page", aria-label, aria-labelledby, aria-describedby, aria-pressed, aria-hidden
  • role="img" sur avatar, role="alert" sur flash messages
  • aria-invalid dynamique sur pagination
  • Lang toggle, steppers, dialogues, search, pagination — tous étiquetés

🌐 Internationalisation

  • Code source 100 % anglais, UI traduite en français via Babel + .po
  • Traductions : rate limiting, messages ARIA, beforeunload, commentaires

⚡ Performance

  • Shiki lazy : langages Shiki passés en imports dynamiques (2.7 MB non chargé au paint)
  • CSS vendor différé : Mantine/BlockNote non bloquants (pattern media="print")
  • Indexes DB : V19 — 4 indexes (FK commentaires, dates de tri)
  • Bulk UPDATE : mask_comments_by_account_id() en 1 requête au lieu de N
  • Read-modify-write supprimé : 6 méthodes compte passent en UPDATE direct

🎨 UI / UX

  • Protection contenu : beforeunload + détection mutations éditeur + interception Back
  • Double-clic mobile Chrome : sélection par mot via double-tap-select.js
  • Double-soumission : form-submit.js désactive le bouton submit au premier clic
  • Thème : script inline synchrone dans <head> → pas de FOUC

…`service_utils.py` is removed since the `dict.get()` call was already safe, and the unused `.back-link:hover` rule in `profile.css` is dropped because it duplicated the global `a:hover` style
…condensed. Env flags are now read from `EnvConfig`, the duplicate `@abstractmethod` is removed, and comment rate‑limiting is handled entirely by `CommentService` with a single `check_rate_limit` entrypoint. The Flask adapter only delegates and tests are updated accordingly
…ow matches author usernames in addition to title and description, aligning its behavior with the port contract and the SQL adapter. An optional `AccountRepository` can be passed to the constructor, when present, `search` and `count_search` iterate accounts and include username matches, otherwise legacy behavior is preserved. Tests are expanded to cover username search and count scenarios
…ss are renamed for consistency, and the SQLAlchemy file adapter now returns a proper DTO with UUID coercion. Imports are updated and tests cover the new DTO flow
…ow exist for file storage and password hashing, with simple in‑memory behavior for saving, retrieving and deleting files, plus deterministic hash and verify logic for passwords. Tests cover all basic operations
…w uses a configurable `template_dir` passed from `app.root_path`, replacing the fragile parent‑directory lookup. `init_web_security` provides the correct path at startup, ensuring the hash computation remains stable even if `flask_setup` is moved
…xt processors and Vite asset helpers are moved from `utils/` into `flask_setup/`, grouping all Flask‑specific code in the same module and resolving architectural issue 23.9. Imports in the application and tests are updated accordingly
…or now exposes `g.current_user` to all templates automatically, removing thirteen repeated `current_user=` arguments across four Flask adapters. Each adapter drops its manual injections and unused imports, and the test suite registers the same processor and updates profile tests to set the current user
…mpt` calls are moved into a single `_init_csrf_exemptions` function in `middleware`, invoked after route registration and resolving endpoints via `app.view_functions`. Routes drop their local exemptions and duplicate `csrf =` lines, and the application now registers the unified exemption list in one place
…logic is moved into `LoginService`, restoring a single‑port adapter. The session port gains photo‑management methods, the service handles all profile and deletion workflows and the Flask adapter becomes a thin delegate. App wiring and tests are updated to match the simplified design
…ssion is replaced with a `scoped_session` to avoid cross‑request leaks in multi‑threaded contexts, with a teardown hook removing the session after each request. The nested locale injector is moved to a module‑level function. Test‑injected sessions are preserved via an early return and a guard flag in the teardown
…dapter no longer inherit from MethodView since their routes are registered directly with `add_url_rule`, making the base class unnecessary
… from project files and the duplicated honeypot logic in the comment adapter is consolidated into a single `_check_honeypot()` helper used by both comment creation and replies
…lete Python methods, the `nl2br` filter, and two SVGs removed. A missing `.alert-warning` style is added, `suneditor-init.js` exposes `window.suneditors` to fix the emoji‑picker crash, and MethodView inheritance is dropped from the registration adapter
…`config.get()` so the shutdown hook can reliably call `session.remove()` on every request. Static paths are now skipped in the user‑identification step, avoiding unnecessary DB access, and tests confirm that `/static/` requests leave `current_user` unset
…ile by expanding to word boundaries on double‑tap and selecting all on triple‑tap, guarded by `e.detail === 2` and a Chrome‑mobile check. The same logic is applied in `double‑tap-select.js`, while Firefox mobile is skipped since it already handles this natively
…ross services, DTOs and adapters, with Pydantic prefixes stripped from both flash and JSON error outputs. Core services now raise native French errors, registration DTO validators are translated and Flask adapters clean up ValidationError messages before displaying them. Output adapters update not‑found and constraint messages, tests are aligned with the new French strings
…tService by dropping the unused `get_all_ordered_by_date_desc` method and eliminating the unused `get_comments_for_article` chain. Related abstract methods, unused imports and their tests are deleted and a missing port import is restored to keep the service consistent
…e input port and made private inside `LoginService`, since it was only used internally by profile‑photo helpers. The abstract method is dropped, the service method is renamed `_update_avatar`
…lidation errors multiple inheritance from both `BlogCommentError` and `ValueError`, keeping Pydantic compatibility while allowing all app‑level comment errors to be caught uniformly. The SQL‑session adapter drops its unused `WeakPasswordError` import and related handling
…atePasswordRequest` DTO, which now handles all validation at the boundary. The service drops its regex logic, he Flask adapter switches to DTO‑based validation and new tests cover both DTO errors and the updated endpoint
…lish across services, adapters, templates, JS and tests. The translation catalog is cleaned up with missing, fuzzy and obsolete entries fixed, and AGENTS.md now reflects the code‑in‑English / po‑in‑French rule
… into a single `_sanitize_comment_content` method in the service. The DTO drops its custom validator, leaving only structural checks, while the service now handles cleaning, emptiness and max length. Redundant blocks in create, reply and edit are removed, and tests are updated accordingly
…rrowed to `except ValidationError`, matching other Flask adapters and preventing real errors from being masked. Only Pydantic `ValidationError` is expected from `FileUpload
…d `_ensure_blocknote_format` helper, replacing duplicated try/except blocks in both `read_article` and `api_get_article`. Two direct unit tests are added to cover the new method
…lidated in both services. CommentService now uses a single `_get_account_and_comment()` helper for delete, edit and hard‑delete, and ArticleService uses `_get_account_and_article()` for update and delete. The shared ownership error message is unified
…st` to match DB limits. All fields must be non‑empty and filename plus content‑type lengths now follow the schema’s maximum sizes. Integration tests cover empty uploads and overly long filenames
…tRole` enum in both article and comment Flask adapters. All comparisons now use `AccountRole.ADMIN` and `AccountRole.AUTHOR`, with missing imports added for consistency and future‑proofing
…decorators, `require_auth` for HTML routes and `require_auth_api` for JSON endpoints. Eleven duplicated blocks are removed, and the last hardcoded role strings are replaced with `AccountRole.ADMIN` and `AccountRole.AUTHOR`. The new `auth_helpers.py` module centralizes this logic, reducing boilerplate and improving consistency
… obsolete `.comment-edited` rule is dropped, and the standalone `video-dict.test.js` (which actually tested `ArticleForm`) is merged into `ArticleForm.test.jsx` before being deleted. The test suite is now cleaner and correctly organized
…e is removed and replaced with a repository‑backed lookup. A new `get_last_comment_timestamp` method is added to the CommentRepository port and implemented in both SQLAlchemy and in‑memory adapters. Tests are updated to mock the repository and to assert the non‑blocking rate‑limit behavior in integration
…ice with its port and Flask adapter. Self‑delete and admin‑delete both run the full cascade with avatar cleanup, comment masking, account removal and session clear. Quality fixes include enum roles, auth decorators, removal of dead CSS, keeping HTML‑only classes, merging tests, DB‑backed rate‑limit lookup, corrected docstrings, a module‑level logger, removal of unused branches, an extracted admin‑role helper, SQL adapter docstring correction with SET NULL tests, and cleanup of a no‑op test call. LoginService is reduced by about 40% and all routes, adapters, templates and tests are updated
…_comment_application.py. Repositories, Services and WebAdapters are now typed structures with attribute access, giving compile‑time typo detection through pyright and removing runtime KeyError risks
…lations are added. CommentEmptyError and CommentTooLongError are dropped from blog_exceptions.py, replaced fully by CommentValidationError. The catalog gains translations for “You must be signed in.” and “Upload failed.” and all message files are regenerated
…articles queries. A new migration creates four indexes on foreign‑key and sort columns to prevent sequential scans. The SQLAlchemy comment model declares indexes on article_id, account_id and posted_at and the article model declares an index on published_at
…nguages and themes. Static lang and theme imports are removed from shiki‑highlighter‑editor.js and the heavy shiki‑langs chunk now loads only when createHighlighter() runs. The generate‑langs script is updated to emit dynamic imports and to fix the output filename and the editor file is regenerated with no static imports
…media="print" onload pattern. BlockNote and Mantine styles load non‑blocking, avoiding the white screen while CSS downloads. Each vite CSS link uses media="print" and an inline onload handler that switches to media="all". The CSP gains the unsafe‑hashes directive plus the stable SHA‑256 hash for the onload handler. base.html applies the new link pattern and middleware updates the script‑src directive
…tead of N writes. The repository provides mask_comments_by_account_id and both adapters implement it. CommentService delegates directly and tests are updated with a second comment plus deleted_by, deleted_at, content and collateral‑damage checks
…tead of read‑modify‑write. update_avatar, update_email, update_password, update_session_token, update_ban_status and update_role switch to filter_by().update()+commit, removing six unnecessary ORM hydrations. update_email wraps the UPDATE and commit in a try/except for IntegrityError because autoflush triggers on update
… suites across adapters, services, DTOs, helpers and SQLAlchemy/in‑memory repos. Pyright goes from four errors to zero with small targeted fixes in session casting, IntegrityError handling, session.remove typing and a corrected argument order. Config is cleaned with coverage.xml added to .gitignore, coverage settings added to pyproject.toml and an error flash added on article delete
… and four code smells are removed. blog_comment_application gets _get_argon2_params, a rate‑breach callback, a stable limiter in app.extensions and a renamed _request_limit param. middleware adds init_rate_limiter with get_remote_address and memory storage and flask‑limiter is added to dependencies. Tests gain rate‑limit fixtures, a direct TestGetArgon2Params, updated factories and a login‑limit integration test. FR translation is added for the rate‑limit message
…emplates and JS. base.html adds aria‑labels, aria‑current, aria‑describedby, role="alert" and aria‑hidden. pagination.js toggles aria‑invalid and theme‑toggle.js sets aria‑pressed. List pages add aria‑labels on search and pagination, the pagination macro marks the active page. login/registration hide <hr>, profile gets role="img" and an aria‑label and FR translations are added for pagination and avatar text
…s. Four dialogs (ban, email, password, jump) gain aria-labelledby pointing to their <h2> title. Two search clear buttons get aria-label. Babel "Clear search" FR translation added. base.html lang-toggle aria-label aligned with existing title attribute
…ditor with instant MutationObserver dirty detection and precise Back‑link interception. ArticleForm tracks initial fields, marks dirty on title/description changes and via ProseMirror mutations, warns on beforeunload and confirms only on real Back links. Dirty state resets on save, templates pass the confirmation string, and FR translations are updated
…m-submit.js disables the submit button on first click with a 10s fallback re-enable timeout. Loaded globally via base.html
@raynaldlao
raynaldlao requested a review from hlargitte July 29, 2026 13:55
@raynaldlao raynaldlao self-assigned this Jul 29, 2026
…onment variables. The workflow now uses correct ARGON2_* and TEST_ARGON2_* secret pairs and adds FLASK_ENV to prevent setup_database crashes. Four new GitHub Secrets are required
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