fix: escape @ in login email placeholder (vue-i18n linked syntax)

Production /login rendered a blank page (browser console: SyntaxError: 10
through the vue-i18n parser). Root cause proved with a RED regression
(RES: vue-i18n public API reproduces 'Invalid linked format' code 10) plus an
independent reviewer: auth.emailPlaceholder="name@company.com" is invalid
vue-i18n linked-message syntax, so createI18n() throws a message-compilation
SyntaxError while LoginView renders t('auth.emailPlaceholder').

Fix: escape the literal at-sign as name{'@'}company.com in th and en so the
message compiles and the visible label is unchanged (name@company.com). Add an
all-translations regression that translates every string in th/en (objects
and arrays) through vue-i18n's public createI18n/global.t API and asserts the
visible placeholder value.

Verification:
- RED test failed at th:auth.emailPlaceholder (code 10) before the fix.
- Independent reviewer verified reproduction + fix, finished PASS.
- Frontend tests 11 passed; production build passed (index-B4oVHpLg.js).
- Chrome headless rendered the login card, Thai heading, and name@company.com
  from the production dist. Artifact checksum hash 3621155075b3d9245d2d05511aaf39b1b0cbcaeea local vs server.
This commit is contained in:
Kunthawat Greethong
2026-09-01 12:11:40 +07:00
parent 16c0996eaf
commit 333f6ccc6e
6 changed files with 98 additions and 3 deletions

View File

@@ -26,6 +26,7 @@ The repository now has a tested identity/authentication foundation, tenant/owner
- Fail-closed scope checks for project, graph, simulation, report, and task IDs in path/query/body.
- Cross-tenant regression tests for project/simulation/report/task access.
- Frontend auth store using cookie sessions, `/login` view, protected workspace router guard, and localized Thai/English login copy.
- Production `/login` white screen root cause fixed locally (2026-09-01): raw `@` in `auth.emailPlaceholder` was invalid vue-i18n linked-message syntax and raised compiler code 10 during render. th/en now use `name{'@'}company.com`; an all-message compiler regression passes. Frontend **11 tests** + production build pass; Chrome rendered DOM contains the login UI. Pending: fresh review, push/redeploy, then live verification.
- Frontend report-status client corrected to POST `{ task_id, simulation_id }`.
- Durable local memory tables and a tenant-scoped SQLAlchemy repository.
- Durable `jobs`, idempotency records, and audit-log schema with migrations `0004_operations` through `0006_job_metadata`; `TaskManager` binds to the current app/session factory, fails closed when an app context has no factory, and has SQL-level tenant/owner filters.

View File

@@ -5,7 +5,7 @@
| Milestone | Status | Last verified | Evidence | Next action |
|---|---|---|---|---|
| Baseline architecture study | complete | 2026-08-23 | `npm run build` passed; `compileall` passed; `git diff --check` passed; source inventory completed | Review MiroFish SaaS plan and lock M0 decisions |
| Thai/English frontend hardening | bounded active-surface gate | 2026-08-24 | Frontend contract tests **10 passed**; production build passed; active routed UI has no hardcoded CJK visible strings; legacy unused `Process.vue` (Chinese copy) retired — router uses `MainView.vue`; mobile viewport capture not yet verified | Migrate remaining legacy views and perform real 320×568 and 500×768 capture when browser permission is available |
| Thai/English frontend hardening | production login root cause fixed; redeploy pending | 2026-09-01 | Root cause of `/login` white screen proved with a RED vue-i18n compiler test: `auth.emailPlaceholder = "name@company.com"` is invalid linked-message syntax and throws compiler code 10 (`Invalid linked format`) while LoginView renders. Escaped as `name{'@'}company.com` in th/en. Recursive compiler regression covers every translation; frontend tests **11 passed**, production build passed (`index-B4oVHpLg.js`), Chrome rendered DOM contains `login-card`, Thai heading, and rendered `name@company.com`; screenshot analysis unavailable because vision provider returned 401 | Complete fresh reviewer gate, then commit/push/redeploy and verify live `/login` |
| Zep replacement | bounded local E2E slice | 2026-08-24 | Local graph → profile → simulation config → report tools → persisted report regression passed; default remains Zep; no full consumer cutover or semantic parity claim | Cut over remaining consumers and close semantic/E2E gaps |
| Auth/tenant/roles | bounded foundation | 2026-08-24 | Identity/session/roles/CSRF/CORS/idempotency/resource guards covered by focused tests; durable task app-state leak fixed; task query filters now push tenant predicates into SQL | Complete broader tenant matrix, admin UI, rate limits, audit/usage policy |
| SaaS foundation batch | in progress | 2026-08-24 | Backend full suite **193 passed** after app/factory isolation, SQLite-FK, auxiliary API auth/CSRF/idempotency, cross-route/multipart idempotency, local consumer-boundary fixes, durable product-resource schema/repository, tenant-scoped `ArtifactStore`, durable `JobQueue`+`worker.py`, versioned redacted `PlatformSettings`, durable `RateLimiter` (wired to login), durable LLM `UsageService`, durable redacted `AuditService`, and durable single-use `PasswordResetService` + endpoints (also covers invite-pending setup); schema/TaskManager regression **16 passed**; auxiliary security **8 passed**; idempotency API **5 passed**; local import-boundary regression **9 fresh-import tests**; product-resource persistence **21 tests**; artifact store **12 tests**; job queue/worker **10 tests**; settings service **4 tests**; rate limiter **6 tests**; usage service **4 tests**; audit service **3 tests**; password reset **6 tests**; frontend gates passed; bounded reviewers passed their exact slices; hardened bases ready; remaining: resource authz matrix completion, admin/bootstrap UI, and deploy topology; `ruff` unavailable; no commit/push/deploy | Complete admin UI, authz matrix, then deploy topology; do not claim full-system approval |

View File

@@ -0,0 +1,60 @@
# 2026-09-01 — Production Login White-Screen Root Cause
## Status
Root cause fixed locally; fresh review and production redeploy pending.
## Incident
Production redirected unauthenticated users to `/login`, then rendered a blank page. The browser console reported:
```text
SyntaxError: 10
... nextToken -> parse
```
Earlier hypotheses around cache, gzip transport, stale assets, and minified line length were disproved: the served JavaScript asset matched the local build byte-for-byte and passed `node --check`.
## Verified root cause
`LoginView.vue` renders `t('auth.emailPlaceholder')`. Both `locales/th.json` and `locales/en.json` contained:
```json
"emailPlaceholder": "name@company.com"
```
vue-i18n treats `@` as linked-message syntax. Direct compiler reproduction returned:
```text
code 10: Invalid linked format
code 14: Unexpected lexical analysis
code 13: Unexpected empty linked key
```
A RED regression test that compiles every Thai and English message failed specifically at `th:auth.emailPlaceholder`, matching production error code 10.
## Fix
- Escape the literal at-sign using vue-i18n syntax: `name{'@'}company.com` in both locales.
- Add a recursive contract test using `@intlify/message-compiler` `baseCompile` for every string in both translation dictionaries.
The rendered translation remains `name@company.com`.
## Verification
- RED focused test: failed at `th:auth.emailPlaceholder` with code 10 before the fix.
- Reviewer cycle 1 failed closed because the initial compiler test skipped strings nested inside locale arrays and used an internal compiler package. The test was corrected to recurse arrays, use vue-i18n's public `createI18n/global.t` API, and assert the visible placeholder value.
- Frontend tests: **11 passed, 0 failed**.
- Production build: **699 modules**, `index-B4oVHpLg.js`, build passed.
- Chrome headless rendered DOM from the production build contains `login-card`, `เข้าสู่ระบบ CrowdSight`, and `name@company.com`.
- Browser runtime log contained no `SyntaxError` or uncaught exception.
- Screenshot file was produced at 1280×800, but image-model analysis was unavailable because the configured vision provider returned HTTP 401; this remains an explicit visual-analysis gap.
- `git diff --check`: passed.
- Static dangerous-pattern scan on added lines: no findings.
## Remaining
1. Obtain a fresh independent reviewer verdict on the current diff.
2. Commit/push after review.
3. Rebuild EasyPanel and verify live `/login` uses the fresh chunk and renders visibly.
4. Mobile visual QA remains deferred as previously agreed.