# RideBus landing ## Development Use Node.js 24 or newer. Install dependencies with `yarn install --immutable`, then run `yarn dev`. ## Next departures Copy `.env.example` to `.env.local` and configure `RIDEBUS_API_KEY` with an authorized RideBus API key. The default API address is `https://api.ridebus.by/`; override it with `RIDEBUS_API_BASE_URL` if needed. Restart the server after changing environment variables. The server route `/api/departures` reads the city catalogue, stops for the selected city, and ETA for the selected stop. Only normalized public fields reach the browser. The API key stays on the server. Cities are cached for one hour, stops for five minutes, and departures for 30 seconds; concurrent requests share the same upstream request. Departure times use `scheduledTime`. Historical delay is removed from the ETA countdown, so the board consistently displays timetable information. The board refreshes every 30 seconds while the tab is visible and shows an unavailable state when the API connection has not been configured. It never substitutes sample data. The city and stop menus use the landing’s custom menu style. Long catalogues have search and a bounded scroll area. Stops with the same name share one menu entry; direction chips retain their separate stop IDs and use the API’s stop `description`. Countdown labels show hours and minutes. Menus support keyboard navigation and Escape, and interface animations respect reduced-motion preferences. The homepage FAQ explains the timetable and links to the contribution page. On touchscreens, opening a menu does not automatically focus search or bring up the keyboard. Tapping search keeps the menu open as the visible viewport changes. Keyboard navigation scrolls only the options, and search text is at least 16px to avoid input zoom on iOS. For Docker Compose, supply `RIDEBUS_API_KEY` and optionally `RIDEBUS_API_BASE_URL` through the shell environment or a local `.env` file. Environment files are excluded from the image build context. Do not commit credentials. ## Server deployment Run the landing as a separate Compose project alongside the API and admin panel: ```sh cp .env.example .env # Set RIDEBUS_API_KEY in .env without committing the file. chmod 600 .env docker compose config --quiet docker compose up -d --build --wait docker compose ps ``` The service publishes port 3000 by default. Point the existing reverse proxy for `ridebus.by` at the server or LXC container's IP and port 3000. TLS is handled by that proxy. Compose does not require Traefik or an existing Docker network. Set `RIDEBUS_PORT` in `.env` to use a different port. Set `RIDEBUS_BIND_ADDRESS` to restrict the listening address; its default is `0.0.0.0` so a reverse proxy in a separate LXC container can reach the site. The image runs the Next.js standalone server as the `node` user, includes an HTTP health check, and receives the API key only at runtime. Updates use `docker compose up -d --build --wait` from the landing's directory. This only updates the `ridebus-landing` project. ## Open Graph images `yarn dev` and `yarn build` generate eight 1200 × 630 PNG images before starting Next.js: one for each of the home, contribution, privacy and terms pages in English and Russian. To regenerate only the images, run `yarn generate:og` (or `npm run generate:og`). Output is written to `public/images/og/{page}-{locale}.png` and is excluded from Git. The Docker build generates and copies it into the standalone image. Each page supplies its own canonical URL, localized Open Graph tags and Twitter large-image card; the image URLs use the production origin `https://ridebus.by`. Edit the page copy and routes in `lib/social.mjs`, and the layout in `lib/og-template.mjs`. The template reuses the landing's bus logo, stars, rounded cards, blue and purple palette, and Manrope/Caveat typography. Local fonts include Latin and Cyrillic characters, so image generation needs no external requests or browser. Font sources and licenses are in `assets/fonts/og/`. In-page anchors such as `/#download` use the homepage preview: crawlers do not request the fragment, so separate previews apply to actual page URLs. ## Checks ```sh yarn test yarn typecheck yarn lint yarn build ```