Commit graph

92 commits

Author SHA1 Message Date
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
Shekhar Vaidya
58d66fe06d docs: update README, About, Privacy and Disclaimer for CF Worker
- README: add cloudflare-worker/ to project structure, update How It
  Works diagram to show CF Worker hop, add CF Worker setup instructions
  under Running Locally, and fix Deployment section (Worker is an
  outbound proxy, not the backend host)
- About: mention CF Worker distributes requests across Cloudflare edge
- Privacy: add Cloudflare as a third-party service, update date
- Disclaimer: update date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:58:07 +05:30