# 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/<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`:

```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`).
