Commit graph

10 commits

Author SHA1 Message Date
nickviljoen
3f124318f9 Phase 4 prep: add Prod cutover runbook
Mirror of DEV_CUTOVER_RUNBOOK.md adjusted for optical-prod:
* Step 1 covers the merge-to-main + tag v3.0.0 done from the laptop
  before any server-side work begins.
* Reflects the gotchas we hit on Dev (per-repo Bitbucket Access Key,
  docker group membership, SSH alias, .env from .env.prod.example,
  Apache Include pattern).
* Adds post-cutover housekeeping: daily DB backup cron, disk-usage
  warning cron, and the sandbox decommission steps for optical-web-1
  after the 1-week soak.
2026-05-09 20:47:05 +02:00
nickviljoen
6b8b8ea5a6 Video Master: revert campaigns folder + lenient name matching
The earlier swap to BOX_CAMPAIGNS_FOLDER_ID=133295752718 was wrong —
Video Master operates on the automation campaigns folder
(156182880490), where subfolders are named by campaign TITLE rather
than the numeric job ID used in Reporting's root.

Reverted the default in config.py and all three .env example files.

Folder naming on Box is inconsistent — '1_CFUL263C01C_Kids drop1' vs
'1_CFUL263C01F-Kids drop 2' vs 'Summer Activation 2026' all coexist.
search_subfolder now strips every non-alphanumeric character from
both the search input and the folder names before substring match,
so:
  "kids drop 1"   →  matches "1_CFUL263C01C_Kids drop1"
  "Spring 2026"   →  matches "4023 Spring 2026"
  "winterfilm"    →  matches "1_WA20263C01 Winter Film"

Form label/placeholder updated to "Campaign Title" with a hint that
spaces/underscores/hyphens/case are all ignored.
2026-05-09 20:19:35 +02:00
nickviljoen
a500d7b088 Six tooling fixes from Dev test pass
Video QC:
* _extract_locale_from_filename now also handles the suffix form
  ..._XX-yy.ext (case-insensitive both sides), so DOOH/OOH-style
  adapt filenames like ..._ES-es.mp4 unblock the price_currency
  check instead of skipping with "could not extract locale".
* Batch results page expires the SQLAlchemy session at the top of
  the route so the post-completion reload sees committed reports
  even when it lands on a different gunicorn worker than the one
  that wrote them. Reload delay bumped 1s → 2s for margin.
* visual_quality prompt now passes the filename's market+language
  to the LLM and tells it the on-screen copy should be in the
  localized language, not the source-language guideline copy.
  Stops Spanish-market videos being flagged as "language mismatch
  with English campaign guidelines".

Printer Check:
* regions.json rewritten to cover all 10 H&M regions (AME, CEU,
  NEU, GCN, IND, SHE, SEU, EEU, EAS, Franchise) with default-all
  groups. Two judgement calls vs the screenshot: kept TR for
  Turkey (TK is Tokelau in ISO and would break filename matching)
  and BR for Brazil (every other code is 2-letter ISO).

Campaign codes:
* New core/utils/campaign_code.py is the single source of truth.
  Matches both the legacy 4-digits-plus-optional-letter (1013A,
  4116) and the new 11-char alphanumeric with year at positions
  5-6 (CFUL263C01D). All four prior parser sites now import from
  this helper.

Video Master:
* BOX_CAMPAIGNS_FOLDER_ID switched 156182880490 → 133295752718
  (same root the Reporting tool uses). Updated config.py default
  and all three .env example files.
* Match page now shows which Box folder the search runs against
  (with a clickable link), and on a not-found error explains what
  was searched for so missing-campaign cases are self-diagnosable.
2026-05-09 18:32:23 +02:00
nickviljoen
0d1d8fd2c9 Apache: move ProxyTimeout out of <Location> (not allowed there)
ProxyTimeout cannot be set inside <Location>. Express the per-route
timeout via the timeout= parameter on ProxyPass instead, matching the
ppt-tool pattern in the optical-dev vhost.
2026-05-09 17:12:09 +02:00
nickviljoen
7622b650af Apache: consolidate dev+prod into single Include-style snippet
Match the convention used by every other app on optical-{dev,prod}:
each app ships one /opt/<app>/deploy/<app>.conf, and the per-host
vhost adds a single `Include` line.

Combines apache-dev.conf and apache-prod.conf (which were identical)
into apache-hm-aiqc.conf. Drops X-Forwarded-Proto and ProxyPreserveHost
since the parent vhost already sets them globally. Raises the body
size to 500MB inside /hm-aiqc/ for video uploads.
2026-05-09 17:05:39 +02:00
nickviljoen
458c75311e Phase 3 prep: add Dev cutover runbook
Self-contained SSH-and-paste guide covering clone → .env → deploy → Apache
reload → smoke test. Includes troubleshooting for the most likely failure
modes (MSAL redirect mismatch, missing env vars, /health 503).
2026-05-09 14:42:12 +02:00
nickviljoen
e772095158 Phase 2: deploy machinery for Dev/Prod cutover
- deploy.sh dev|prod with --dry-run, auto-rollback if /health fails
  within 60s; checkpoint saved to .last_deploy_rollback before reset
- deploy/rollback.sh last|<sha> with the same Docker compose dance
- deploy/health-check.sh — curl wrapper for monitoring/oncall
- deploy/apache-{dev,prod}.conf — Location blocks proxying /hm-aiqc/
  to gunicorn on 127.0.0.1:5050 with X-Script-Name set so wsgi.py's
  ReverseProxied middleware emits prefixed URLs
- deploy/.env.{dev,prod}.example — starter envs with Azure SSO config
2026-05-09 14:08:06 +02:00
nickviljoen
84326352b2 Phase 1: replace local username/password auth with Azure AD SSO
Lifted JWT-cookie auth pattern from the AI QC sibling project:
  core/auth/middleware.py validates Azure AD JWTs and stores them in
  an httpOnly cookie (hm_aiqc_auth_token). Tenant membership is
  enforced by JWTValidator's tid check, which is sufficient for the
  tenant-wide access policy chosen for this project.

  templates/login.html now drives an MSAL.js popup that POSTs the
  ID token to /auth/login. base.html exposes Azure config to all
  pages so the logout button can also clear the MSAL session.

  app.py's @before_request now checks the JWT cookie and exposes
  g.user; modules read user identity via core.auth.current_user_email
  so usage logs and created_by columns now record the signed-in
  user's email rather than a session value.

  Legacy username/password code removed: top-level auth_middleware.py,
  jwt_validator.py, deploy/generate_password.py.
2026-05-09 13:59:29 +02:00
nickviljoen
e69f077c79 Add Dev/Prod migration + SSO plan
Captures the four-phase plan to move HM QC from the shared sandbox to
dedicated Dev/Prod servers with Azure AD SSO, mirroring the AI QC sibling
project's pattern. Includes locked-in decisions (URL path, branch strategy,
shared Entra app, fresh-start data), file-by-file lift list from AI QC,
phased checklist, and the IT ticket text. Action deferred to late April.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:09:37 +02:00
nickviljoen
f21e41afc3 v1.2.0: Add Docker deployment, simplify auth to local login, production config
- Add Dockerfile, docker-compose.yml, .dockerignore for containerised deployment
- Add deploy/ scripts (deploy.sh, nginx/apache configs, password generator)
- Replace MSAL/Azure AD auth with local username/password authentication
- Add login.html template
- Simplify app.py, middleware, and auth routes for production use
- Update gunicorn_config.py and wsgi.py for Docker/production
- Update templates to work with new auth and URL prefix handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 14:37:53 +02:00