# Active Context — Strava Frontend ## Task State - task_id: TASK-SSR-DETAIL - status: success - parent_task: TASK-SSR-2 - summary: **SSR /public/workouts/:id — HTML-контент переписан под DOM-структуру WorkoutItem.vue (вёрстка + фото + OG:image + noscript).** - next task: — (no active tasks) ### TASK-F12/F13/F14 — фиксы карты тренировки (WorkoutItem.vue) - Ф12: `position="top-center left-center"` (центрирование маркера на координате); `findNearestTrackIndex` (радиус 40 м) вместо точного lookup `getKey`/`coordWithIndex`. - Ф13: стабильные `:settings` фото-маркеров через Map-кэш (`getPhotoMarkerSettings`) — одного этого оказалось НЕДОСТАТОЧНО (Vue патчит VNode-ы и при неизменённых пропсах). - Ф14: `v-memo="[photo.id]"` на фото-маркерах — жёсткий пропуск патча, `onUpdated`/`clearElement()` больше не срабатывают. Методология и причины записаны в `systemPatterns.md` (секция «Charts & maps»). ## TASK-404: HTTP 404 для несуществующих страниц (SEO) ### Проблема Любой неизвестный URL (например `/random-page`) отдаёт `200` + redirect на `/explore`. Поисковик путается. ### Решение 1. `server/index.ts` — catch-all 404 handler: `app.use((req, res) => { if (req.method === "GET") res.status(404).send(renderTemplate(...)) })` с SEO-мета (title: "404 — страница не найдена — Cycle Rider", noindex meta). 2. `nginx.conf` — whitelist SPA-роутов (`/workouts`, `/auth`, `/preferences`, `/404`) → `try_files $uri /index.html`; всё остальное (кроме SSR-локаций) → `proxy_pass http://127.0.0.1:3001`. 3. `src/router/index.ts` — catch-all `/:pathMatch(.*)*` → redirect на `{ name: "404" }` (вместо `explore`). 4. `src/router/seo.ts` — запись `404: { title: "404 — страница не найдена — Cycle Rider", description: "..." }` в SEO_MAP. ### Acceptance Criteria - `curl -s -o /dev/null -w "%{http_code}" http://localhost:80/random-path` → `404` - `curl -s http://localhost:80/random-path | grep -c '404'` → > 0 - `curl -s -o /dev/null -w "%{http_code}" http://localhost:80/workouts` → `200` - `curl -s -o /dev/null -w "%{http_code}" http://localhost:80/auth/login` → `200` - `curl -s -o /dev/null -w "%{http_code}" http://localhost:80/explore` → `200` - `yarn lint` → 0, `yarn build` → 0 ## ✅ CODER SUCCESS REPORT — TASK-404 - `server/index.ts` — добавлен catch-all 404 handler (`app.use(...)` без path) перед `app.listen`: для GET — `renderTemplate` с SEO-мета (title: "404 — Страница не найдена — Cycle Rider", canonical `/404`), для остальных методов — `res.status(404).send("Not Found")`. - `nginx.conf` — реорганизация: добавлен SPA whitelist `~ ^/(workouts|auth|preferences|404)(/|$)` → `try_files $uri /index.html`; старое `location /` (SPA fallback) заменено на catch-all → `proxy_pass http://127.0.0.1:3001` (SSR вернёт 404 для неизвестных путей). - `src/router/index.ts` — catch-all `/:pathMatch(.*)*` redirect: `{ name: "explore" }` → `{ name: "404" }`. - `src/router/seo.ts` — добавлена запись `"404"` в `SEO_MAP` (title + description). - Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0 errors, vite build ✓ 7.02s). ## ✅ CODER SUCCESS REPORT — TASK-F11 - `package.json` — `@unhead/vue` ^3.4.1 в dependencies (установлено через `npm install @unhead/vue --legacy-peer-deps`; peer-конфликт vite 4 vs vite 5 разрешён legacy-flag'ом; vite в project поднят до 5.4.21 транзитивно). - Создан `src/router/seo.ts` — `setupSeo(router, head: Unhead)`: SEO_MAP по name роута + DEFAULT_SEO, `head.push({title, meta:[description]})` + `router.afterEach(() => update())`. - `src/main.ts` — `import { createHead } from "unhead/client"` + `import { headSymbol } from "@unhead/vue"`; `const head = createHead(); app.provide(headSymbol, head); setupSeo(router, head);` (после `app.use(router)`). - `index.html` — `