Parent: #97
Scaffolds the routing + dispatch layer that every per-type page hangs off. Includes the legacy redirect from /security/{sym} for non-stocks so existing URLs (watchlist, ideas, bookmarks) keep working.
Scope
internal/server/server.go
- New handlers
handleCrypto, handleIndex, handleForex, handleETF. Mirror existing handleSecurity (server.go:273) — extract symbol, render the per-type template. Register routes alongside /graph/{symbol} (server.go:202).
- Modify
handleSecurity: look up the type via s.news.GetProfile(...) (already used in handleSecurityProfile, server.go:334). If type ≠ stock, http.Redirect(w, r, "/<type>/<sym>", 301).
internal/store/store.go
- New tiny
security_types(symbol PK, type, fetched_at) table + accessors. Used to cache the per-symbol type result so the legacy redirect doesn't profile-fetch on every hit.
internal/server/templates/
- Stubs:
crypto.html, index.html, forex.html, etf.html. Each scaffolds the same shell as security.html but renders "Coming soon" inside the tab strip until the per-type vertical-slice PR fills it in.
internal/server/static/info-shared.js (new)
- Extract from
info.js: mdInline / mdBlock helpers (info.js:120-170), the AI/trading tab loader + render path, the news-tab loader (parameterised by allowed categories), the top EOD chart slide-in. Stock page keeps loading these via the shared file (no functional regression).
internal/server/static/terminal.js
- Extend the
COMMANDS table (terminal.js:50-61) with crypto, index, forex, etf entries — needsSecurity: true, paths /<type>/{symbol}.
- New
viewForExchange(exchange, symbol) helper mirroring detectSecurityType.
- Search-submit (terminal.js:1032 area) +
navigate(...) (terminal.js:70): route via the Exchange field on the SearchResult.
- In-memory
symbolType map seeded by every search result so navigate('info', sym) from non-search code paths (watchlist, ideas, AI competitor cards) dispatches correctly. Fallback: /security/{sym} → server 301.
tests/e2e/security_routing_test.go (new)
GET /crypto/BTCUSD → 200
GET /security/BTCUSD → 301 → /crypto/BTCUSD
GET /security/AAPL → 200, no redirect
docs/TODO-security-types.md
Out of scope
Per-type page content. Those land in their own PRs.
Parent: #97
Scaffolds the routing + dispatch layer that every per-type page hangs off. Includes the legacy redirect from
/security/{sym}for non-stocks so existing URLs (watchlist, ideas, bookmarks) keep working.Scope
internal/server/server.gohandleCrypto,handleIndex,handleForex,handleETF. Mirror existinghandleSecurity(server.go:273) — extract symbol, render the per-type template. Register routes alongside/graph/{symbol}(server.go:202).handleSecurity: look up the type vias.news.GetProfile(...)(already used inhandleSecurityProfile, server.go:334). If type ≠ stock,http.Redirect(w, r, "/<type>/<sym>", 301).internal/store/store.gosecurity_types(symbol PK, type, fetched_at)table + accessors. Used to cache the per-symbol type result so the legacy redirect doesn't profile-fetch on every hit.internal/server/templates/crypto.html,index.html,forex.html,etf.html. Each scaffolds the same shell assecurity.htmlbut renders "Coming soon" inside the tab strip until the per-type vertical-slice PR fills it in.internal/server/static/info-shared.js(new)info.js:mdInline/mdBlockhelpers (info.js:120-170), the AI/trading tab loader + render path, the news-tab loader (parameterised by allowed categories), the top EOD chart slide-in. Stock page keeps loading these via the shared file (no functional regression).internal/server/static/terminal.jsCOMMANDStable (terminal.js:50-61) withcrypto,index,forex,etfentries —needsSecurity: true, paths/<type>/{symbol}.viewForExchange(exchange, symbol)helper mirroringdetectSecurityType.navigate(...)(terminal.js:70): route via theExchangefield on the SearchResult.symbolTypemap seeded by every search result sonavigate('info', sym)from non-search code paths (watchlist, ideas, AI competitor cards) dispatches correctly. Fallback:/security/{sym}→ server 301.tests/e2e/security_routing_test.go(new)GET /crypto/BTCUSD→ 200GET /security/BTCUSD→ 301 →/crypto/BTCUSDGET /security/AAPL→ 200, no redirectdocs/TODO-security-types.mdOut of scope
Per-type page content. Those land in their own PRs.