fast-website
Lightweight, SEO-first website for SanthoshJ that aggregates YouTube + Instagram + podcast content and tracks conversion events via Umami.
Local Setup
cd site
cp .env.example .env
npm install
npm run fetch-content
npm run dev
Open http://localhost:4321.
Configuration
All configuration is via environment variables (see site/.env.example).
YouTube
YOUTUBE_CHANNEL_IDis required for ingestion.- Optional:
YOUTUBE_API_KEYenables view-count ingestion for high-performing ranking.
Notes:
- The channel ID looks like
UC...and can be found by viewing the channel page source or via YouTube Studio settings. - If
YOUTUBE_API_KEYis not set, ingestion falls back to the public channel RSS feed and metrics will be missing.
Podcast
PODCAST_RSS_URLis required to ingest episodes.
Instagram (Embed-First)
Edit site/content/instagram-posts.json:
{
"posts": [
{ "url": "https://www.instagram.com/p/<id>/", "publishedAt": "2026-02-01T00:00:00Z", "title": "Movie post" }
]
}
Phase 2 (API ingestion) path:
- Requires Meta developer app setup, correct account type, token issuance + refresh, and platform policy compliance.
- Recommended approach: keep embed-first for v1, then add an optional API ingest module when constraints are clear.
Curation / Featured Videos
If YouTube metrics are not available, curate high-performing videos in site/content/featured-videos.json:
{ "videoIds": ["abc123", "def456"] }
Analytics (Umami)
Set:
PUBLIC_UMAMI_SCRIPT_URLPUBLIC_UMAMI_WEBSITE_ID
If either is missing, analytics is disabled and the site still functions.
Event tracking is implemented using Umami's data-umami-event and data-umami-event-* attributes.
Click Tracking Taxonomy (Umami-Compatible)
All tracked clickables MUST set:
data-umami-event: the event name (max 50 chars), e.g.click,cta_click,outbound_clickdata-umami-event-target_id: stable unique identifier for the clickable element (namespaced likenav.*,cta.*,card.*)data-umami-event-placement: where the clickable appears (e.g.nav,hero,section_header,home.newest)
For links, also set:
data-umami-event-target_url: destination URL (can be relative for internal navigation)
Common additional properties:
data-umami-event-domain: destination domain (for outbound links)data-umami-event-source:youtube/instagram/podcastwhere applicabledata-umami-event-ui_placement: a sub-placement for UI grouping (e.g.content_card)
Instrumentation checklist:
- Every clickable should have a unique
target_idso it can be segmented in Umami. - Use categorical ids and placements only (no PII).
Deployment (Linode + Docker)
Build and run:
docker compose build
docker compose up -d
The container serves the static output on port 8080 (map or proxy as needed).
Refreshing Content (Manual)
Content is fetched at build time into site/content/cache/content.json.
On the Linode host:
./scripts/refresh.sh
This:
- Runs
npm run fetch-contentinsite/ - Rebuilds the Docker image
- Restarts the container
Refreshing Content (Scheduled)
Install a daily cron using deploy/cron.example as a starting point.
Rollback:
- Re-run
docker compose up -dwith a previously built image/tag, or restore the last known-good repo state and rerunscripts/refresh.sh.