All checks were successful
Deploy / deploy (push) Successful in 3m58s
- Extract world-parse + packet capture into @albion/shared (reused by server and a new standalone agent workspace). - Migrate the data layer from SQLite/better-sqlite3 to shared Postgres (async pg with a ?->$n helper; economy engine preloads the item/recipe graph so its sync recursion still works). - API-key auth (ALBION_API_KEY) on all /api routes except health; web UI sends it via a fetch wrapper + key-entry gate; server serves the built UI with a path-traversal-safe static handler (drops @fastify/static). - Windows companion agent: pushes world map + captured market orders to /api/ingest/*; runs on the gaming PC where the game traffic lives. - Docker + docker-compose (external proxy + backend networks for Nginx Proxy Manager and shared Postgres) and a Forgejo deploy workflow. - Validated end-to-end against Postgres 16 and a full compose run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
32 lines
1.3 KiB
TypeScript
32 lines
1.3 KiB
TypeScript
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
|
|
/**
|
|
* Optional API-key gate. When ALBION_API_KEY is set (required for any public
|
|
* deployment), every /api route except health needs `Authorization: Bearer <key>`
|
|
* or `?apiKey=<key>`. When unset (localhost dev), the API is open.
|
|
*
|
|
* For real internet exposure, ALSO put this behind HTTPS + a private network
|
|
* (Tailscale/WireGuard) — an API key alone is a minimum, not a full solution.
|
|
*/
|
|
export function registerAuth(app: FastifyInstance): void {
|
|
const key = process.env.ALBION_API_KEY?.trim();
|
|
if (!key) {
|
|
app.log.warn('ALBION_API_KEY not set — API is UNAUTHENTICATED (fine for localhost, unsafe if exposed).');
|
|
return;
|
|
}
|
|
|
|
app.addHook('onRequest', async (req: FastifyRequest, reply: FastifyReply) => {
|
|
const url = req.url.split('?')[0];
|
|
// allow health checks and the static web UI without a key
|
|
if (url === '/api/health' || !url.startsWith('/api/')) return;
|
|
|
|
const header = req.headers.authorization;
|
|
const bearer = header?.startsWith('Bearer ') ? header.slice(7).trim() : undefined;
|
|
const queryKey = (req.query as { apiKey?: string })?.apiKey;
|
|
const provided = bearer ?? queryKey;
|
|
|
|
if (provided !== key) {
|
|
return reply.code(401).send({ error: 'unauthorized — missing or invalid API key' });
|
|
}
|
|
});
|
|
}
|