Commit graph

93 commits

Author SHA1 Message Date
Shekhar Vaidya
bf5dca1fe4 feat: Phase 4 web-to-CLI handoff UX
- Add CliHandoff component: shows install link + pre-filled msdl command
- Highlighted card (Recommended) shown inline on Sentinel/WAF errors
- Collapsed version shown below OfficialFallback on stale links
- Rename instructions with platform-specific filenames (msdl.exe / msdl)
- Fix backend CORS to allow local network origins (100.x, 192.168.x)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:16:43 +05:30
Shekhar Vaidya
6bc2785994 feat(cli): formatted download output and contribute message
Show the ISO filename above each link with a separator rule so the
output is easier to read. Updated contribute message to reference
MSDL Web App with URL instead of bare domain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:21:19 +05:30
Shekhar Vaidya
36c875ab23 fix: use extractRawExpiry for /contribute expiry validation
parseLinkExpiry has a 1h safety floor that causes it to treat already-
expired URLs as valid. Replace with extractRawExpiry + direct se
timestamp comparison so any URL expiring < 1h out is rejected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:12:36 +05:30
Shekhar Vaidya
9a5f41290d feat: Phase 3 crowdsourced cache contribution
- Redis L2 persistent cache: initRedis() from REDIS_URL env, seed
  in-memory linkCache on startup, write-through on every fresh fetch
  and background refresh (silent fallback to memory-only if unavailable)
- POST /contribute endpoint: validates product catalog, expiry > 1h,
  Microsoft CDN host allow-list, per-IP token bucket (~5/min),
  CONTRIBUTE_SECRET header; writes to in-memory + Redis; logs all
  accepts and rejections
- CLI --no-contribute flag (also MSDL_NO_CONTRIBUTE=1 env var):
  background goroutine posts raw MS JSON to /contribute after every
  consumer fetch; best-effort 5s timeout; opt-out message to stderr

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:07:35 +05:30
Shekhar
7ebf5a9560
feat: msdl CLI — direct Windows ISO downloads from user's machine (Phase 2)
feat: msdl CLI — direct Windows ISO downloads from user's machine (Phase 2)
2026-06-18 13:13:04 +05:30
Shekhar
72ad55eda8
feat: web graceful degradation for Microsoft WAF blocks (Phase 1)
feat: web graceful degradation for Microsoft WAF blocks (Phase 1)
2026-06-18 13:09:58 +05:30
Shekhar Vaidya
9acc5dbf31 feat(cli): interactive language picker for eval downloads 2026-06-18 13:08:05 +05:30
Shekhar Vaidya
4cc344e666 chore(winget): fill SHA256 for v0.1.0 Windows binary 2026-06-18 12:47:04 +05:30
Shekhar Vaidya
ad7ebb312f fix(ci): check WINGET_TOKEN in shell, not in workflow if condition 2026-06-18 12:43:10 +05:30
Shekhar Vaidya
17691b0c79 chore(winget): initial manifest for starkSV.msdl v0.1.0 2026-06-18 12:17:00 +05:30
Shekhar Vaidya
5c7f805f41 ci: GitHub Actions release workflow for msdl CLI binaries 2026-06-18 12:11:56 +05:30
Shekhar Vaidya
cdf64f4c3e feat(cli): main flow with interactive and named-arg modes 2026-06-18 12:09:56 +05:30
Shekhar Vaidya
2f93726ec9 fix(cli): guard empty slices in pickers, propagate scanner I/O errors 2026-06-18 12:01:13 +05:30
Shekhar Vaidya
e8a4be2698 feat(cli): interactive numbered picker (stdin/stderr separated) 2026-06-18 11:53:59 +05:30
Shekhar Vaidya
09c3b2b175 feat(cli): direct Microsoft client (session, SKU, links, eval scrape) 2026-06-18 11:45:14 +05:30
Shekhar Vaidya
86f9b685de feat(cli): product catalog with word-based search 2026-06-18 11:39:09 +05:30
Shekhar Vaidya
9a860022fd feat(cli): scaffold Go module 2026-06-18 11:36:33 +05:30
Shekhar Vaidya
123643a947 feat: use short links for guide URLs in OfficialFallback; add Win 8.1 guide
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:29:38 +05:30
Shekhar Vaidya
3cd8528430 feat: add tech-latest guide links to official fallback component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:20:42 +05:30
Shekhar Vaidya
77056cb50f feat: Phase 1 graceful degradation for WAF-blocked links
- Backend: emit X-MSDL-Link-Status (fresh/cached/stale) and
  X-MSDL-Link-Expires headers on every /proxy response; expose
  both via Access-Control-Expose-Headers
- Frontend: read headers in handleGetLinks/handleRefreshLinks;
  show amber stale warning when backend is WAF-blocked
- Frontend: new OfficialFallback component (collapsible) with
  step-by-step guide to Microsoft's official download page,
  pre-filled with the user's selected language; shown on stale
- Plan: add Redis/Valkey persistence section to Phase 3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:13:46 +05:30
Shekhar Vaidya
5d1d402715 chore: remove product 48 (Win 8.1 Single Language) — Microsoft removed it server-side
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:59:39 +05:30
Shekhar Vaidya
eb0fc51fe2 chore: remove debug cookie-count log from proxy handler
Diagnostic log added during Sentinel investigation — no longer needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:09:26 +05:30
Shekhar Vaidya
04a4d7e58c debug: log cookie count in jar before proxy request
Temporary diagnostic — tells us whether Set-Cookie headers from
Microsoft are actually reaching the backend through the CF Worker.
If count is 0, the Worker is stripping them or MS sets cookies via JS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:50:12 +05:30
Shekhar Vaidya
6f2c02d79b fix: forward cookies through CF Worker to satisfy Microsoft Sentinel
Sentinel started rejecting all /proxy requests because cookies set during
the fingerprinting flow (vlscppe/ov-df) were never reaching the
GetProductDownloadLinksBySku call.

Two gaps closed:
- CF Worker now forwards Cookie header to Microsoft and Set-Cookie headers
  back to the backend
- Backend uses a simpleCookieJar (domain-scoping disabled) shared across
  the full session: setup → warmup → download link fetch. Standard
  cookiejar rejects microsoft.com cookies when the request URL is the
  Worker domain.

Tested locally without Worker (direct MS calls): download links returned,
no Sentinel error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:36:43 +05:30
Shekhar Vaidya
04e745272b chore: mark v1.2.0 shipped in CLAUDE.md and PROGRESS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 22:14:29 +05:30
Shekhar
56549b7201
Merge pull request #17 from starkSV/feat/ux-improvements
feat: expiry countdown, refresh links, CLI tabs, recently viewed
2026-05-22 21:56:07 +05:30
Shekhar Vaidya
b2c9e2d874 feat: expiry countdown, refresh links, CLI tabs, recently viewed
- Backend: ?force=true on /proxy bypasses cache for fresh link fetch
- Frontend: live expiry countdown from `se` param; falls back to static 24h for consumer links
- Frontend: Refresh button appears when link expires in <6h, fires force fetch
- Frontend: CliCommand replaces Aria2Tip — wget/curl/aria2 tabs, persists in localStorage, on all pages
- Frontend: RecentlyViewed on homepage — localStorage, tracks consumer + eval pages, shows expired state
- chore: add .claude/ to .gitignore; remove FileSize (not returned by Microsoft CDN)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:55:10 +05:30
Shekhar Vaidya
0557339e72 chore: remove .claude/ from git tracking, add to .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:07:10 +05:30
Shekhar Vaidya
1c54c36d03 feat: expiry countdown, refresh links, CLI tabs, recently viewed, file size support
- Backend: ?force=true on /proxy bypasses cache for fresh link fetch
- Frontend: parse `se` param for live expiry countdown on download links
- Frontend: show Refresh button when link expires in <6h, force-fetches fresh URL
- Frontend: CliCommand component replaces Aria2Tip — wget/curl/aria2 tabs, persists selection in localStorage
- Frontend: RecentlyViewed component — localStorage, shows active countdown or expired state
- Frontend: FileSize shown alongside arch if returned by Microsoft CDN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:06:17 +05:30
Shekhar Vaidya
e3f34016c6 chore: plan frontend UX improvements — expiry countdown, refresh, CLI tabs, recently viewed, file size
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 20:54:55 +05:30
Shekhar Vaidya
17315ad4e9 docs: add PROGRESS.md with full shipping history and production metrics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:27:19 +05:30
Shekhar Vaidya
efe0c241ab chore: update CLAUDE.md — mark #12 and Beyond #12 items complete, defer rate limiter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:34:21 +05:30
Shekhar Vaidya
825c825686 docs: document caching architecture, /metrics endpoint, and env vars in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:29:42 +05:30
Shekhar
ca4a89589b
Merge pull request #16 from starkSV/feat/metrics
feat: /metrics endpoint, cache eviction, and observability fixes
2026-05-18 23:24:07 +05:30
Shekhar Vaidya
90c498233b fix: route background stale refresh through singleflight; cleanup logging
- Background refresh on stale serve now goes through sfGroup.Do so
  concurrent stale hits collapse into one Microsoft call instead of
  hammering the API from multiple goroutines simultaneously.

- cleanupCaches: only logs when entries are actually evicted (was
  logging every 30min regardless of whether anything changed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:12:59 +05:30
Shekhar Vaidya
a0a6341d5f feat: add /metrics endpoint, fix cache eviction and review issues
- /metrics: atomic counters for requests, cache hits, MS fetches, neg
  hits, stale serves, and cache sizes per endpoint. Auth via
  METRICS_SECRET env var (query param or Authorization: Bearer).

- Cache eviction: cleanupCaches() goroutine runs every 30min, removes
  expired SKU/neg entries and link entries past ExpiresAt + 4h stale
  window. Prevents unbounded map growth under sustained traffic.

- rand: replace global rand with package-level rand.Rand to avoid
  mutex contention at high QPS (jitter() uses local source).

- Singleflight keys: separate sfKey from negKey in both handlers for
  clarity — "sku:<id>" and "link:<id>:<sku>" vs neg cache keys.

- Background refresh on stale: when /proxy serves a stale entry after
  a failed refresh, fires a goroutine to retry and update the cache
  so the next request gets fresh data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:08:43 +05:30
Shekhar
3329cd1b52
Merge pull request #15 from starkSV/feat/caching-layer
fix: bump Dockerfile to golang:1.25-alpine to match go.mod requirement
2026-05-18 22:52:10 +05:30
Shekhar Vaidya
2f14ca6f22 fix: bump Dockerfile to golang:1.25-alpine to match go.mod requirement 2026-05-18 22:51:25 +05:30
Shekhar
8f7bbdeed9
Merge pull request #14 from starkSV/feat/caching-layer
feat: two-layer in-memory caching for backend (closes #12)
2026-05-18 22:49:33 +05:30
Shekhar Vaidya
c6d8615cf1 feat: two-layer in-memory caching for backend (closes #12)
Reduces Microsoft API traffic from thousands/day to ~50-100/day,
eliminating the 715-123130 rate-limit block under real traffic.

1. Singleflight (golang.org/x/sync/singleflight) — wraps all Microsoft
   fetches so 500 concurrent cache misses collapse into 1 API hit.

2. SKU info cache — 7-day TTL keyed by product_id. Language lists are
   stable; no need to hit Microsoft on every product page load.

3. Download link cache — dynamic TTL keyed by product_id:sku_id. Expiry
   is derived from the signed URL's `se` query param minus 30min buffer,
   so we never serve an expired signed URL.

4. Negative response caching — 60s TTL for 429 / 715-123130 failures.
   Prevents thundering herd from retries worsening an existing block.
   Only stored when no stale data is available to fall back on.

5. Dynamic TTL — parses `se` (signed expiry) from Microsoft CDN URL:
   TTL = (se - now) - 30min. Falls back to 22h if unparseable.

6. Stale-on-failure — if a refresh fails (rate-limited or transient),
   the expired cache entry is served temporarily. Applies to both
   /proxy (download links) and /evallinks (eval ISOs).

7. Jitter — ±5min random offset on all TTLs prevents synchronised
   mass-expiry spikes when many entries are warmed at the same time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:41:58 +05:30
Shekhar
ddf96a873d
Merge pull request #13 from starkSV/feat/evalcenter-isos
feat: Enterprise & Server evaluation ISOs via Microsoft Eval Center
2026-05-18 22:21:20 +05:30
Shekhar Vaidya
590382b127 docs: update README for eval ISOs feature
- Add Server & Enterprise ISOs to "What it does" bullets
- Document /evallinks API endpoint with example response
- Add evaluation editions section to Supported Products table
- Fix Contributing guide to reflect current architecture (products.json,
  no PRODUCT_META/RELATED_GROUPS) and add steps for adding eval products

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:18:02 +05:30
Shekhar Vaidya
ddc657e0a7 fix(seo): audit and fix SEO across all pages
- ProductsPage: align <title> with og:title ("All Windows ISO Releases")
- Disclaimer + PrivacyPolicy: add noindex (legal/thin pages shouldn't rank)
- ProductDetailPage: improve auto-generated meta description wording;
  add SoftwareApplication JSON-LD to match EvalDetailPage
- HomePage: update JSON-LD description to include Windows Server

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:14:52 +05:30
Shekhar Vaidya
fe1191b07f fix(ui): mention Server & Enterprise in HowItWorks step 1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:10:37 +05:30
Shekhar Vaidya
4ad8cfbfcc feat: update ComparisonTable and FAQAccordion to reflect eval ISOs
Add "Server & Enterprise ISOs" row to the Why MSDL comparison table
and a new FAQ entry explaining eval editions, the 180-day trial, and
where to find them. Closes #eval-polish.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:08:55 +05:30
Shekhar Vaidya
b1458934c3 fix(ui): standardise page widths to two-tier system
Listing pages (home, products, eval): max-w-4xl
Content pages (product detail, eval detail, about, privacy, disclaimer): max-w-2xl

Previously four different widths were in use across the app.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 22:04:09 +05:30
Shekhar Vaidya
fdf4d7258d fix(seo): improve meta tags and JSON-LD for eval product pages
- Add unique seoDesc field to each eval product (≤155 chars)
- EvalDetailPage: use seoDesc for description, add Evaluation to
  title, add SoftwareApplication JSON-LD alongside breadcrumb
- EvalPage: add ItemList JSON-LD for the listing page

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 22:01:55 +05:30
Shekhar Vaidya
a78059187f feat: add Enterprise & Server eval ISOs via Microsoft evalcenter
Adds /eval listing page and /product/<slug> detail pages for Windows
Server 2025/2022/2019/2016 and Windows 11 Enterprise evaluation ISOs,
sourced from Microsoft evalcenter without any session flow.

Backend: new /evallinks endpoint fetches evalcenter page, extracts
fwlinks, follows all redirects in parallel, detects arch + locale.
24h cache per product with startup warming so first hit is instant.

Frontend: EvalPage listing at /eval, EvalDetailPage at /product/<slug>
matching consumer structure with downloads, aria2 template, system
requirements, and also-available section. Smart ProductRouter in
App.tsx dispatches slug vs numeric productId. Dock highlights Products
tab for /product/* and /eval routes. CTA banner on home page and
footer link added. sitemap.xml updated with 6 new URLs.

Closes #13

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 21:56:57 +05:30
Shekhar Vaidya
d6d782bc49 chore: add CLAUDE.md with project context and implementation backlog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:32:18 +05:30
Shekhar
38ea92d014
Merge pull request #11 from starkSV/feat/cf-worker-proxy
docs: update README, About, Privacy and Disclaimer for CF Worker
2026-05-18 12:58:47 +05:30