Jul 13 - Optimize code 2

This commit is contained in:
2026-07-13 17:07:30 -04:00
parent 00a9ba7770
commit a5c800e87c
6 changed files with 182 additions and 161 deletions
+31 -11
View File
@@ -42,7 +42,7 @@ Each category has subcategories and a JSON-driven `field_schema`
| Reverse proxy / static | Nginx |
| Process manager | systemd |
| OS | Ubuntu 22.04 Server |
| Background jobs | APScheduler or RQ (expiry sweep, email, image processing) |
| Background jobs | systemd timers (expiry/boost/keyword sweeps). RQ planned — not yet installed |
| Payments | Stripe (Billing + Payment Intents + Tax + Customer Portal) |
| Email | SMTP relay via Brevo (smart relay, existing infra) |
| i18n | Flask-Babel |
@@ -188,7 +188,8 @@ classifieds/
│ │ │ # moderator_required
│ │ ├── security.py # hash_password, verify_password, generate_token,
│ │ │ # read_token (itsdangerous)
│ │ ── text.py # normalize() — accent-insensitive (phở→pho, ñ→n)
│ │ ── text.py # normalize() — accent-insensitive (phở→pho, ñ→n)
│ │ └── time.py # utcnow() — naive-UTC helper (replaces datetime.utcnow)
│ │
│ ├── templates/
│ │ ├── base.html # Layout: responsive nav, OG/meta blocks, toast JS,
@@ -710,6 +711,9 @@ Phase 6: flag threshold → auto-flip, keyword blocklist, reports queue, audit_l
- Contact masking for low-trust users
- Signed time-limited tokens for email verify + password reset
- ProxyFix for correct `is_secure` + client-IP behind Nginx
- Mid-session ban enforcement: `enforce_active_account` before_request hook logs
out any authenticated user whose `status != active` on their next request
(Flask-Login only checks `is_active` at login time)
- Stripe webhook signature verification on every event
- Secrets via `.env` only — never committed
- HTTPS only in prod; `SESSION_COOKIE_SECURE=True` in ProdConfig
@@ -830,6 +834,8 @@ accounts. If `caching_sha2_password` errors: `pip install cryptography`.
# 2. python3 -m venv venv && pip install -r requirements.txt
# 3. cp .env.example .env && nano .env (no inline comments!)
# 4. flask db upgrade && python seed.py
# 4b. Compile translations (committed .mo travel with the repo, but recompile
# after any .po change): pybabel compile -d app/translations
# 5. Copy systemd units:
cp deploy/classifieds.service /etc/systemd/system/
cp deploy/classifieds-expire.{service,timer} /etc/systemd/system/
@@ -848,7 +854,9 @@ certbot --nginx -d classifieds.ngodanguyen.tech
Nginx config independently of DNS
- DNS A record must point to server's public IP
- `curl -4 ifconfig.me` vs `dig +short classifieds.ngodanguyen.tech` must match
- CSS cache busting: copy to `style.v2.css` and update `base.html` link
- CSS cache busting: bump the `?v=N` query string on the `style.css` link in
`base.html` (single `style.css` file; the old `style.vN.css` copy approach was
dropped)
---
@@ -861,6 +869,9 @@ certbot --nginx -d classifieds.ngodanguyen.tech
- All datetimes UTC in DB. Localize only at render.
- Money: integer cents everywhere. Never `float` for currency.
- `db.session.get(Model, pk)` not `Model.query.get(pk)` (SA 2.0 deprecated).
- UTC now: use `utcnow()` from `app/utils/time.py` — never `datetime.utcnow()`
(deprecated on Python 3.12+). The helper returns a naive UTC datetime to match
the naive `DateTime` columns; do not mix in tz-aware datetimes.
- Enums: DB `ENUM` columns + Python `str, enum.Enum` in `models/enums.py`.
- Validate `listings.attributes` against `field_schema` server-side on every save.
- i18n: all user-facing strings in `_()`. No hardcoded English in templates.
@@ -1063,9 +1074,12 @@ certbot --nginx -d classifieds.ngodanguyen.tech
- [ ] Listing preview before publish
**i18n completion**
- [ ] Extract all `_()` strings to `.pot`
- [ ] Translate VI + ES `.po` files
- [ ] Compile `.mo`, test all three locales
- [x] Translate VI + ES `.po` files (75-string Phase 12 catalog: auth + listings chrome)
- [x] Compile `.mo` for vi + es; verified loadable via gettext (placeholders + plural-forms intact)
- [x] `.mo` files un-gitignored so they deploy (see Known Issues)
- [ ] Re-extract `.pot` to capture Phase 37 strings (messaging/payments/admin) —
needs `pybabel extract` (Babel not currently installed); then translate + recompile
- [ ] Test all three locales end-to-end in the running app
---
@@ -1115,11 +1129,17 @@ certbot --nginx -d classifieds.ngodanguyen.tech
| `.env` inline comments | MUST NOT use `# comments` after values — python-dotenv does not strip them → `int()` ValueError on startup. |
| Stripe webhook | `/billing/webhook` is `@csrf.exempt`. Always verify `Stripe-Signature` header first. |
| MySQL user grants | Must create BOTH `@'localhost'` AND `@'127.0.0.1'` — MySQL treats them as different accounts. |
| CSS cache busting | Nginx serves static with 30d expires. To bust: copy to `style.vN.css` and update `base.html` link. |
| CSS cache busting | Nginx serves static with 30d expires. To bust: bump the `?v=N` query string on the `style.css` link in `base.html`. |
| Translations (.mo) | `*.mo` is gitignored **except** `app/translations/**` (negated) so compiled catalogs deploy with the code — the no-build-step deploy has no compile phase. Recompile with `pybabel compile -d app/translations` after editing any `.po`. |
| Python / datetime | Target 3.113.12 for prod parity. `datetime.utcnow()` is deprecated on 3.12+ — use `utcnow()` from `app/utils/time.py`. |
---
_End of spec. Phase 16 complete, all smoke-test checks green.
Phase 7 (Polish) in progress — SEO, UX, email notifications, and reviews done.
Remaining: metro landing pages, i18n .po compilation, Redis sessions, WebP thumbnails,
listing lightbox, load-more, "back to results", translation cache, weekly digest._
_End of spec. Phase 16 complete. Phase 7 (Polish) in progress — SEO, UX, email
notifications, reviews, and VI/ES translation compilation done (existing catalog).
Remaining: metro landing pages, i18n re-extraction for Phase 37 strings, Redis
sessions, WebP thumbnails, listing lightbox, load-more, "back to results",
translation cache, weekly digest.
Note: run the smoke suite in a venv matching `requirements.txt` (Flask 3.0.3 /
Python 3.113.12); the ambient Python 3.14 env lacks `flask_login` and drifts from
the pins._