5.6 KiB
News feed
The launcher's News tab fetches ${MAIN_VITE_SERVER_URL}/news.json and renders the entries on the landing screen. The endpoint is dynamic: it mirrors the same forum posts the website's homepage shows in its "Recent forum posts" cards, so updating the forum updates the launcher.
Endpoint
GET ${MAIN_VITE_SERVER_URL}/news.json → 200 application/json
MAIN_VITE_SERVER_URL comes from main/.env at build time. With the current production setup that resolves to the public site origin (e.g. https://octowow.st/news.json).
The route is served by Laravel (routes/web.php → news.json) and reads from App\Services\ForumFeedService, which fetches the configured phpBB Atom feed (FORUM_FEED_BASE_URL/FORUM_FEED_MODE/FORUM_FEED_FORUM_ID in config/customs.php → forum_feed). The same service backs the homepage's recent-forum-posts Livewire component, so what shows in the launcher is exactly what shows on the site.
No auth. The launcher times out after 8 seconds and validates the body against the schema below — malformed payloads surface as the "Couldn't reach the news feed" error state (with a Try again button).
Payload contract
{
"items": [
{
"id": "2026-04-24-launch", // required, stable, used as React key
"title": "Welcome to the new client", // required
"date": "2026-04-24", // required, anything Date.parse() accepts
"body": "Multi-line\nbody text supported.", // required, \n preserved
"author": "example", // optional
"url": "https://example.com/changelog" // optional, must be a full URL
}
]
}
Source of truth for the schema: src/common/schemas.ts (NewsItemSchema, NewsFeedSchema). If you change the contract, update both ends.
Notes:
itemsis rendered in the order returned — sort newest-first on the server.bodyis rendered as plain text withwhitespace-pre-wrap. No HTML/markdown.url, when present, becomes a "Read more" button that opens in the user's default browser viashell.openExternal. Skip it for inline-only posts.idshould never change for an existing post (stable React keys, future bookmarking/read-state).
Publishing
There is no static file to edit anymore. To change what the launcher shows, post on the forum (FORUM_FEED_BASE_URL, e.g. https://octowow.st/forum). The next launcher fetch picks it up subject to two cache layers:
forum_feed.cache_ttl(default 600 s, envFORUM_FEED_CACHE_TTL) — Laravel server-side cache of the parsed Atom feed.Cache-Control: public, max-age=120on the/news.jsonresponse — short edge cache so launcher launches in a burst don't all hit Laravel.
The launcher's react-query cache also holds for 5 minutes per session; users can hit the refresh icon in the News header to force a re-fetch (which still hits the two cache layers above).
Tuning what shows
- Which forum / mode shows: set
FORUM_FEED_MODE(topics_active|topics|news|forum) and, ifforum,FORUM_FEED_FORUM_IDin the website container's environment. - How fresh: lower
FORUM_FEED_CACHE_TTLfor fresher news at the cost of more upstream forum fetches. Pair with the route's 120-secondCache-Controlif you also want to relax the edge cache. - How many items: the route currently caps at 10; the homepage shows 3. Edit the
recent(10)argument inroutes/web.php→news.jsonto change the launcher cap independently of the homepage.
Testing
Confirm Laravel is serving it:
curl -s ${MAIN_VITE_SERVER_URL}/news.json | jq .
Expected: a {"items": [...]} body. An empty items: [] means the forum feed is reachable but has nothing matching the configured mode (or the cache is still warm with an empty result — bust it by php artisan cache:clear inside the website container, or wait FORUM_FEED_CACHE_TTL seconds).
No items / errors:
{"items": []}—FORUM_FEED_BASE_URLis unset, the feed returned non-2xx, the body wasn't parseable Atom XML, or the configured forum has no posts. Check the website container'sstorage/logs/laravel.logforForumFeedServicewarnings.Couldn't reach the news feedin the launcher — Laravel returned a 5xx (route exception, missingForumFeedServicebinding) or the schema validator rejected the body. Check the launcher's main-process log at%APPDATA%\octo-launcher\logs\main.logforMalformed news feed.
End-to-end check in the launcher:
- Open the launcher (the News tab is the default view when no other tab is selected).
- Click the refresh icon in the News header.
- Entries should appear within ~1 second once Laravel + the forum cache are warm.
Failure modes the launcher already handles
| Server response | UI behaviour |
|---|---|
200 with valid JSON |
Renders entries |
200 with empty items: [] |
"No news yet — check back later." |
200 with malformed JSON or missing required fields |
Error state + Try again. Reason logged in main-process logs (%APPDATA%\octo-launcher\logs\main.log). |
404, 5xx, network unreachable, > 8s timeout |
Error state + Try again. |
You don't need to ship a placeholder news.json to avoid 404s — the empty/error state is intentional.
Where the code lives
- Main-process fetcher + schema validation: src/main/api/routers/news.ts
- Schema: src/common/schemas.ts (
NewsItemSchema,NewsFeedSchema) - Renderer UI: src/renderer/components/tabs/NewsTab.tsx
- Router wiring: src/main/api/root.ts (
news)