Compare commits

..

3 Commits

Author SHA1 Message Date
Aarnav Tale 6c83e96341 chore: v0.5.2 2025-02-28 13:07:14 -05:00
Aarnav Tale 03e6a26b3a fix: don't reload the prod bundle on refresh 2025-02-28 13:06:38 -05:00
Aarnav Tale 5b716ab5ce chore: update docs to reflect 0.5.1 2025-02-28 12:22:20 -05:00
5 changed files with 60 additions and 8 deletions
+3
View File
@@ -1,3 +1,6 @@
### 0.5.2 (February 28, 2025)
- Hotfixed an issue where the server bundle got reloaded on each request
### 0.5.1 (February 28, 2025)
- Fixed an issue that caused the entire server to crash on start
- Fixed the published semver tags from Docker
+35
View File
@@ -0,0 +1,35 @@
# Headplane Agent
The Headplane agent is a lightweight service that runs alongside the Headscale server.
It's used to interface with devices on your network locally, unlocking the following:
- **Node Information/Status**: View Tailscale versions, OS versions, and connection details.
- **SSH via Web (Soon)**: Connect to devices via SSH directly from the Headplane UI.
It's built on top of [tsnet](https://tailscale.com/kb/1244/tsnet), the official
set of libraries published by Tailscale for creating local services that can
join the tailnet.
While it isn't required to run the agent, it's highly recommended to get the
closest experience to the SaaS version of Tailscale. This is paired with the
integrations provided by Headplane to manage DNS and Headscale settings.
### Installation
The agent can either be ran as a standalone binary or as a Docker container.
Agent binaries are available on the [releases](https://github.com/tale/headplane/releases) page.
The Docker image is available through the `ghcr.io/tale/headplane-agent` tag.
The agent requires the following environment variables to be set:
- **`HP_AGENT_HOSTNAME`**: A hostname you want to use for the agent.
- **`HP_AGENT_TS_SERVER`**: The URL to your Headscale instance.
- **`HP_AGENT_TS_AUTHKEY`**: An authorization key to authenticate with Headscale (see below).
- **`HP_AGENT_HP_SERVER`**: The URL to your Headplane instance.
- **`HP_AGENT_HP_AUTHKEY`**: The generated auth key to authenticate with Headplane.
If you already have Headplane setup, you can generate all of these values within
the Headplane UI. Navigate to the `Settings` page and click `Agent` to get started.
HP_AGENT_HOSTNAME=headplane-agent
HP_AGENT_TS_SERVER=http://localhost:8080
#HP_AGENT_AUTH_KEY=3e0cd749021e5984267cde4b0a5a2ac32c1859e56f7911aa
HP_AGENT_TS_AUTHKEY=a4dab065c735cb4eae4f12804cf7e111206f9c7c9247c629
HP_AGENT_HP_SERVER=http://localhost:3000/admin
+2 -2
View File
@@ -34,7 +34,7 @@ Here is what a sample Docker Compose deployment would look like:
services:
headplane:
# I recommend you pin the version to a specific release
image: ghcr.io/tale/headplane:0.5.0
image: ghcr.io/tale/headplane:0.5.1
container_name: headplane
restart: unless-stopped
ports:
@@ -148,7 +148,7 @@ spec:
serviceAccountName: default
containers:
- name: headplane
image: ghcr.io/tale/headplane:0.5.0
image: ghcr.io/tale/headplane:0.5.1
env:
# Set these if the pod name for Headscale is not static
# We will use the downward API to get the pod name instead
+1 -1
View File
@@ -19,7 +19,7 @@ Here is what a sample Docker Compose deployment would look like:
services:
headplane:
# I recommend you pin the version to a specific release
image: ghcr.io/tale/headplane:0.5.0
image: ghcr.io/tale/headplane:0.5.1
container_name: headplane
restart: unless-stopped
ports:
+19 -5
View File
@@ -6,6 +6,8 @@ import {
writeReadableStreamToWritable,
} from '@react-router/node';
import mime from 'mime/lite';
import type { RequestHandler } from 'react-router';
import type { ViteDevServer } from 'vite';
import appContext from '~server/context/app';
import { loadDevtools, stacksafeTry } from '~server/dev/hot-server';
import prodBuild from '~server/prod-handler';
@@ -16,18 +18,30 @@ declare global {
const __hp_prefix: string;
}
export const listener: RequestListener = async (req, res) => {
// biome-ignore lint/suspicious/noExplicitAny: Who cares man
let devtools: { server: ViteDevServer; handler: any } | undefined = undefined;
let prodHandler: RequestHandler | undefined = undefined;
let loaded = false;
async function loadGlobals() {
await hp_loadConfig();
const devtools = import.meta.env.DEV ? await loadDevtools() : undefined;
const prodHandler = import.meta.env.PROD ? await prodBuild() : undefined;
const baseDir = resolve(join('./build', 'client'));
devtools = import.meta.env.DEV ? await loadDevtools() : undefined;
prodHandler = import.meta.env.PROD ? await prodBuild() : undefined;
loaded = true;
}
const baseDir = resolve(join('./build', 'client'));
export const listener: RequestListener = async (req, res) => {
if (!loaded) {
await loadGlobals();
}
const url = new URL(`http://${req.headers.host}${req.url}`);
// build:strip
if (devtools) {
await new Promise((resolve) => {
devtools.server.middlewares(req, res, resolve);
devtools?.server.middlewares(req, res, resolve);
});
}