# fast-website Lightweight, SEO-first website for SanthoshJ that aggregates YouTube + Instagram + podcast content and tracks conversion events via Umami. ## Local Setup ```bash 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_ID` is required for ingestion. - Optional: `YOUTUBE_API_KEY` enables 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_KEY` is not set, ingestion falls back to the public channel RSS feed and metrics will be missing. ### Podcast - `PODCAST_RSS_URL` is required to ingest episodes. ### Instagram (Embed-First) Edit `site/content/instagram-posts.json`: ```json { "posts": [ { "url": "https://www.instagram.com/p//", "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`: ```json { "videoIds": ["abc123", "def456"] } ``` ## Analytics (Umami) Set: - `PUBLIC_UMAMI_SCRIPT_URL` - `PUBLIC_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_click` - `data-umami-event-target_id`: stable unique identifier for the clickable element (namespaced like `nav.*`, `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` / `podcast` where applicable - `data-umami-event-ui_placement`: a sub-placement for UI grouping (e.g. `content_card`) Instrumentation checklist: - Every clickable should have a unique `target_id` so it can be segmented in Umami. - Use categorical ids and placements only (no PII). ## Deployment (Linode + Docker) The production host is intentionally minimal and only needs Docker (no Node.js on the server). ### Local Docker ```bash docker compose build docker compose up -d ``` The container serves the static output on port `8080` (map or proxy as needed). ### Production (Docker-Only Host) In production, CI builds and publishes a Docker image (nginx serving the static output). The server updates by pulling that image and restarting the service. Runbook: `deploy/runbook.md`. ### Refreshing Content (Manual, Docker-Only) Content is fetched at build time into `site/content/cache/content.json` (typically in CI), then packaged into the image. On the server host: ```bash ./scripts/refresh.sh ``` This: 1. Pulls the latest published image 2. Restarts the service (no build on the host) ### Refreshing Content (Scheduled) Install a daily cron using `deploy/cron.example` as a starting point. Rollback: - Re-deploy a known-good image tag/digest (see `deploy/runbook.md`).