Installer: support Forgejo as catalog source; NPM dashboard bound to internal IP

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Ezequiel C. 2026-09-11 00:02:49 +02:00
parent d66db4e85e
commit 3c994af96b
4 changed files with 27 additions and 4 deletions

View file

@ -159,6 +159,17 @@ installer authenticates its API and raw requests automatically: `HOMELAB_GITHUB_
`GITHUB_TOKEN` / `HOMELAB_TOKEN`). The repository must be pushed — the installer reads from GitHub, `GITHUB_TOKEN` / `HOMELAB_TOKEN`). The repository must be pushed — the installer reads from GitHub,
not from your local checkout. not from your local checkout.
**Forgejo (self-hosted)**: set `HOMELAB_API_BASE` and `HOMELAB_RAW_BASE` (any value other than the
GitHub defaults switches the installer to Forgejo URL/API patterns). Example — the homelab's own
Forgejo at `http://10.0.0.5:3000` (repo `Homelab/repo`, branch `master`):
```bash
export HOMELAB_OWNER=Homelab HOMELAB_REPO=repo HOMELAB_BRANCH=master
export HOMELAB_API_BASE=http://10.0.0.5:3000/api/v1
export HOMELAB_RAW_BASE=http://10.0.0.5:3000
bun run src/index.ts list
```
Adding a new service means adding a folder plus its `metadata.json` and regenerating the catalog: Adding a new service means adding a folder plus its `metadata.json` and regenerating the catalog:
```bash ```bash

View file

@ -12,11 +12,17 @@ export interface Config {
verbose: boolean; verbose: boolean;
env: Record<string, string>; env: Record<string, string>;
token?: string; token?: string;
apiBase: string;
rawBase: string;
forgejo: boolean;
} }
const GITHUB_API = "https://api.github.com";
export function loadConfig(cli: Partial<Config> = {}): Config { export function loadConfig(cli: Partial<Config> = {}): Config {
const envLocal = process.env.HOMELAB_LOCAL; const envLocal = process.env.HOMELAB_LOCAL;
const localPath = cli.localPath ?? (envLocal || undefined); const localPath = cli.localPath ?? (envLocal || undefined);
const apiBase = process.env.HOMELAB_API_BASE ?? GITHUB_API;
return { return {
owner: cli.owner ?? process.env.HOMELAB_OWNER ?? "ReinadoRojo", owner: cli.owner ?? process.env.HOMELAB_OWNER ?? "ReinadoRojo",
@ -33,6 +39,9 @@ export function loadConfig(cli: Partial<Config> = {}): Config {
process.env.HOMELAB_GITHUB_TOKEN ?? process.env.HOMELAB_GITHUB_TOKEN ??
process.env.GITHUB_TOKEN ?? process.env.GITHUB_TOKEN ??
process.env.HOMELAB_TOKEN, process.env.HOMELAB_TOKEN,
apiBase,
rawBase: process.env.HOMELAB_RAW_BASE ?? "https://raw.githubusercontent.com",
forgejo: apiBase !== GITHUB_API,
}; };
} }

View file

@ -7,11 +7,14 @@ import { parseAndValidate } from "./metadata.ts";
const UA = "homelab-installer/1.0"; const UA = "homelab-installer/1.0";
function rawUrl(cfg: Config, path: string): string { function rawUrl(cfg: Config, path: string): string {
return `https://raw.githubusercontent.com/${cfg.owner}/${cfg.repo}/${cfg.branch}/${path}`; if (cfg.forgejo) {
return `${cfg.rawBase}/${cfg.owner}/${cfg.repo}/raw/branch/${cfg.branch}/${path}`;
}
return `${cfg.rawBase}/${cfg.owner}/${cfg.repo}/${cfg.branch}/${path}`;
} }
function apiUrl(cfg: Config, path: string): string { function apiUrl(cfg: Config, path: string): string {
return `https://api.github.com/repos/${cfg.owner}/${cfg.repo}/${path}`; return `${cfg.apiBase}/repos/${cfg.owner}/${cfg.repo}/${path}`;
} }
function authHeaders(cfg: Config, scheme: "token" | "bearer"): Record<string, string> { function authHeaders(cfg: Config, scheme: "token" | "bearer"): Record<string, string> {

View file

@ -16,7 +16,7 @@
"ports": [ "ports": [
"80:80", "80:80",
"443:443", "443:443",
"81:81" "10.0.0.5:81:81"
], ],
"volumes": [ "volumes": [
"npm_data:/data", "npm_data:/data",
@ -31,5 +31,5 @@
"networks": { "networks": {
"homelab": { "external": true } "homelab": { "external": true }
}, },
"notes": "Dashboard at http://<host>:81 — first login admin@example.com / changeme (change it immediately, and restrict port 81 or apply an access list). Add Proxy Hosts like streaming.example.com -> multistreaming:8080 (enable Websockets) or auth.example.com -> authelia:9091; use the container/service name as the forward hostname since everything shares the homelab network. All proxy hosts, users, and certs persist in the npm_data and npm_letsencrypt volumes, so they survive restarts and updates. RTMP ingest is not HTTP, so it does NOT go through NPM: OBS connects directly to the multistreaming container's published port 1935 (rtmp://feed.streaming.example.com:1935/live/<stream-key>). SSO forward-auth for apps without OIDC: on the proxy host, add a custom location /authelia pointing to http://authelia:9091/api/authz/forward-auth with headers X-Original-URL $scheme://$http_host$request_uri, X-Forwarded-Proto $scheme, X-Forwarded-Host $http_host, X-Forwarded-Uri $request_uri, and then add `auth_request /authelia;` plus `auth_request_set $user $upstream_http_remote_user;` and `proxy_set_header Remote-User $user;` in the Advanced tab." "notes": "Dashboard at http://10.0.0.5:81 (LAN only — port 81 is bound to the internal IP) — first login admin@example.com / changeme (change it immediately). Add Proxy Hosts like streaming.example.com -> multistreaming:8080 (enable Websockets) or auth.example.com -> authelia:9091; use the container/service name as the forward hostname since everything shares the homelab network. All proxy hosts, users, and certs persist in the npm_data and npm_letsencrypt volumes, so they survive restarts and updates. RTMP ingest is not HTTP, so it does NOT go through NPM: OBS connects directly to the multistreaming container's published port 1935 (rtmp://feed.streaming.example.com:1935/live/<stream-key>). SSO forward-auth for apps without OIDC: on the proxy host, add a custom location /authelia pointing to http://authelia:9091/api/authz/forward-auth with headers X-Original-URL $scheme://$http_host$request_uri, X-Forwarded-Proto $scheme, X-Forwarded-Host $http_host, X-Forwarded-Uri $request_uri, and then add `auth_request /authelia;` plus `auth_request_set $user $upstream_http_remote_user;` and `proxy_set_header Remote-User $user;` in the Advanced tab."
} }