Versioned Folder 0.0.33

Sanitized install and API guide

Omni API

A documentation-first build for weather, time, Docker status, and guarded email delivery. This page is read-only on purpose: it explains each command, each endpoint, and the UI integration path without exposing secrets.

Install

Quick Start

Use the safe template, install packages, start the app, then open the docs page at http://localhost:3000/.

Step 1

Copy the env template

Create a private .env file from the sanitized example.

PowerShell
Copy-Item .env.example .env
Bash
cp .env.example .env
Step 2

Install dependencies

This downloads the packages defined in package.json.

npm
npm install
Step 3

Start the service

Launch the API and the docs UI on your configured port.

npm
npm start

Config

Environment Variables

Only placeholders belong in docs. Put real values in .env or your deployment secret manager.

Variable Required Example Purpose
PORT No 3000 Express server port.
LOG_LEVEL No info Pino logging level.
WEATHER_API_KEY Yes for live weather your_weather_api_key Authenticates requests to WeatherAPI.
WEATHER_LOCATIONS No Portland, ME|Wallkill, NY Default watchlist loaded at startup.
REFRESH_INTERVAL_MS No 600000 Weather refresh cadence in milliseconds.
WEATHER_LOCATIONS_FILE No ./data/weather-locations.json Persisted watchlist file path.
SEND_EMAIL_AUTH Yes for email your_email_token Auth token accepted by x-auth or Authorization: Bearer.
SMTP_HOST Yes for email smtp.example.com SMTP hostname used during startup verification and sending.
SMTP_PORT Yes for email 587 SMTP port.
SMTP_SECURE Yes for email false true for implicit TLS, false for standard SMTP or STARTTLS.
SMTP_USER Yes for email [email protected] SMTP username.
SMTP_PASS Yes for email your_smtp_password SMTP password.
SMTP_FROM Yes for email [email protected] Default sender address.
SMTP_REQUIRE_TLS No true Require TLS when the server supports it.
SMTP_ALLOW_SELF_SIGNED No false Non-production escape hatch for self-signed certificates.
DOCKER_SOCKET No \\\\.\\pipe\\docker_engine Socket or named pipe path used by Docker container listing.
Weather note: if your current WeatherAPI key is invalid, live weather fetches will return 401. Replace the key in .env and restart the app.

Commands

Local And Docker Commands

Each command below is safe to copy. Replace only the placeholder values in your private environment file.

Local

Smoke check the running app

Use these first to confirm the service boots and the docs page is available.

curl
curl http://localhost:3000/time
curl http://localhost:3000/weather/watchlist
PowerShell
Invoke-RestMethod -Uri "http://localhost:3000/time"
Invoke-RestMethod -Uri "http://localhost:3000/weather/watchlist"
Docker

Build and run locally

Build a generic local image and mount a persistent data volume.

docker build
docker build -t omni-api:0.0.33 .
docker run
docker run -d \
  --name omni-api \
  -p 3000:3000 \
  --env-file .env \
  -v omni-api-data:/usr/src/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e DOCKER_SOCKET=/var/run/docker.sock \
  --restart unless-stopped \
  omni-api:0.0.33

Reference

Endpoint Guide

Every endpoint below includes purpose, payload rules, response shape, and copy-ready examples for command line and UI code.

GET /time Current server time.

No auth required. Useful for health checks and “last updated” UI badges.

curl
curl http://localhost:3000/time
PowerShell
Invoke-RestMethod -Uri "http://localhost:3000/time"
fetch
const response = await fetch("/time");
const data = await response.json();
document.querySelector("#server-time").textContent = data.locale;

Example response

{
  "iso": "2026-05-12T18:52:30.847Z",
  "locale": "5/12/2026, 2:52:30 PM",
  "timestamp": 1778611950847
}
GET /weather Full weather payload for every watched location.

No auth required. This is the most detailed weather response and is best suited for internal dashboards.

curl
curl http://localhost:3000/weather
fetch
const response = await fetch("/weather");
const forecasts = await response.json();
GET /weather/all Summary weather data for all watched locations.

No auth required. Returns lighter data than /weather, making it a better fit for public-facing cards.

curl
curl http://localhost:3000/weather/all
fetch
const response = await fetch("/weather/all");
const summary = await response.json();
summary.forEach((item) => {
  console.log(item.location, item.data.current.temp_f);
});

Example response

[
  {
    "id": 0,
    "location": "Portland, ME",
    "data": {
      "location": { "name": "Portland" },
      "current": { "temp_f": 58.6 }
    }
  }
]
GET /weather/watchlist Current watchlist and refresh settings.

No auth required. Use this to populate a UI list before loading individual weather records.

curl
curl http://localhost:3000/weather/watchlist
PowerShell
Invoke-RestMethod -Uri "http://localhost:3000/weather/watchlist"
fetch
const response = await fetch("/weather/watchlist");
const watchlist = await response.json();
renderWatchlist(watchlist.locations);

Example response

{
  "refreshIntervalMs": 600000,
  "freshDebounceMs": 5000,
  "locations": [
    { "id": 0, "location": "Portland, ME" },
    { "id": 1, "location": "Wallkill, NY" }
  ]
}
POST /weather/watchlist Add a watched location and fetch it immediately.

No auth required. Request body must include location.

curl
curl -X POST http://localhost:3000/weather/watchlist \
  -H "Content-Type: application/json" \
  -d "{\"location\":\"Hollis Center, ME\"}"
PowerShell
Invoke-RestMethod `
  -Method Post `
  -Uri "http://localhost:3000/weather/watchlist" `
  -ContentType "application/json" `
  -Body '{"location":"Hollis Center, ME"}'
fetch
await fetch("/weather/watchlist", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ location: "Hollis Center, ME" })
});
GET /weather/:id Full weather payload for one watched location.

No auth required. Replace :id with a numeric watchlist ID.

curl
curl http://localhost:3000/weather/0
fetch
const response = await fetch("/weather/0");
const result = await response.json();
GET /weather/fresh/:id Immediate weather refresh for one watched location.

No auth required. Use this when you need to bypass the short debounce window.

curl
curl http://localhost:3000/weather/fresh/0
fetch
const response = await fetch("/weather/fresh/0");
const freshData = await response.json();
GET /docker/liststate Docker container state from the configured socket.

No auth required. This endpoint works only when the Docker socket or named pipe is accessible to the app.

curl
curl http://localhost:3000/docker/liststate
fetch
const response = await fetch("/docker/liststate");
const dockerState = await response.json();

Example response

{
  "containers": [
    {
      "id": "abc123",
      "image": "nginx:latest",
      "state": "running",
      "status": "Up 3 hours"
    }
  ]
}
POST /sendemail Send or schedule email with verified SMTP configuration.

Requires x-auth or Authorization: Bearer. Canonical request fields are to, subject, body, and delaySeconds. Legacy fields Subject, Body, and timedelay are still accepted.

curl
curl -X POST http://localhost:3000/sendemail \
  -H "Content-Type: application/json" \
  -H "x-auth: your_email_token" \
  -d "{\"to\":\"[email protected]\",\"subject\":\"Hello\",\"body\":\"Testing email delivery\",\"delaySeconds\":0}"
PowerShell
Invoke-RestMethod `
  -Method Post `
  -Uri "http://localhost:3000/sendemail" `
  -Headers @{ "x-auth" = "your_email_token" } `
  -ContentType "application/json" `
  -Body '{"to":"[email protected]","subject":"Hello","body":"Testing email delivery","delaySeconds":0}'
fetch
await fetch("/sendemail", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-auth": "your_email_token"
  },
  body: JSON.stringify({
    to: "[email protected]",
    subject: "Hello",
    body: "<strong>Testing email delivery</strong>",
    delaySeconds: 0
  })
});

Success response

{
  "status": "sent",
  "messageId": "<[email protected]>",
  "accepted": ["[email protected]"],
  "rejected": []
}
If startup SMTP verification fails, this endpoint now returns 503 with {"status":"unavailable","error":"Email service unavailable"} instead of using risky silent fallbacks.

Frontend

UI Integration Recipes

These examples are ready to drop into a browser-based UI. The public docs page stays read-only, but your own app can call the JSON endpoints directly.

Widget

Server time badge

Refresh a “last seen” or “server time” label.

fetch
async function loadServerTime() {
  const response = await fetch("/time");
  const data = await response.json();
  document.querySelector("#server-time").textContent = data.locale;
}
Dashboard

Weather watchlist view

Load the watchlist first, then decide which detailed endpoint to call next.

fetch
async function loadWatchlist() {
  const response = await fetch("/weather/watchlist");
  const data = await response.json();
  return data.locations;
}
Form

Email submission

Send canonical fields from a UI form while keeping the auth token outside the public page.

fetch
async function sendEmail(emailToken, formValues) {
  const response = await fetch("/sendemail", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-auth": emailToken
    },
    body: JSON.stringify({
      to: formValues.to,
      subject: formValues.subject,
      body: formValues.body,
      delaySeconds: formValues.delaySeconds ?? 0
    })
  });

  if (!response.ok) {
    throw new Error("Email request failed");
  }

  return response.json();
}

Support

Troubleshooting And Security Notes

The app stays up even when external integrations are unavailable, but the failing endpoint will tell you why.

Weather returns 401

Your WeatherAPI key is invalid or expired. Replace WEATHER_API_KEY in .env and restart the process.

Email returns 503

SMTP settings are missing or startup verification failed. Check every required SMTP variable and confirm the certificate chain is valid.

Git history cleanup

This workspace does not include .git metadata, so past secret exposure must be cleaned in the real source repository, not only in this folder.