From 4d48239abdf08391c917c0dc10b06c9dce887000 Mon Sep 17 00:00:00 2001 From: Federico Jaramillo Martinez Date: Sat, 9 May 2026 22:06:42 +0200 Subject: [PATCH] Add configurable Pi Web data directory --- README.md | 4 +++- src/config.ts | 10 ++++++++++ src/server/sessiond/config.ts | 4 ++-- src/server/storage/projectStore.test.ts | 13 +++++++++++++ src/server/storage/projectStore.ts | 16 +++++++++++++--- 5 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 src/server/storage/projectStore.test.ts diff --git a/README.md b/README.md index 7b92432..4adec23 100644 --- a/README.md +++ b/README.md @@ -220,10 +220,12 @@ Environment variables: - `PI_WEB_PORT` / `PORT` — web server port. Defaults to `8504`. - `PI_WEB_HOST` — web server bind host. Defaults to `127.0.0.1`. -- `PI_WEB_SESSIOND_SOCKET` — Unix socket path used by both the daemon and web process when `PI_WEB_SESSIOND_URL` is not set. Defaults to `~/.pi-web/sessiond.sock`. +- `PI_WEB_DATA_DIR` — Pi Web data directory. Defaults to `~/.pi-web`. +- `PI_WEB_SESSIOND_SOCKET` — Unix socket path used by both the daemon and web process when `PI_WEB_SESSIOND_URL` is not set. Defaults to `$PI_WEB_DATA_DIR/sessiond.sock`. - `PI_WEB_SESSIOND_PORT` — optional TCP port for the daemon. If unset, the daemon listens on the Unix socket instead. - `PI_WEB_SESSIOND_HOST` — daemon TCP bind host when `PI_WEB_SESSIOND_PORT` is set. Defaults to `127.0.0.1`. - `PI_WEB_SESSIOND_URL` — daemon URL used by the web process when connecting over TCP, for example `http://127.0.0.1:3001`. If you set `PI_WEB_SESSIOND_PORT`, set this for the web process too. +- `PI_WEB_PROJECTS_FILE` — optional override for the projects storage JSON file. Defaults to `$PI_WEB_DATA_DIR/projects.json`. ## systemd user services diff --git a/src/config.ts b/src/config.ts index 03061a7..c2f46fc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,6 +24,16 @@ export function defaultPiWebConfigPath(env: NodeJS.ProcessEnv = process.env): st return join(xdgConfigHome !== undefined && xdgConfigHome !== "" ? xdgConfigHome : join(homedir(), ".config"), "pi-web", "config.json"); } +export function defaultPiWebDataDir(): string { + return join(homedir(), ".pi-web"); +} + +export function piWebDataDir(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): string { + const configured = env["PI_WEB_DATA_DIR"]; + if (configured === undefined || configured === "") return defaultPiWebDataDir(); + return resolve(cwd, configured); +} + export function piWebConfigPath(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): string { const configured = env["PI_WEB_CONFIG"]; if (configured === undefined || configured === "") return defaultPiWebConfigPath(env); diff --git a/src/server/sessiond/config.ts b/src/server/sessiond/config.ts index 0a9d32b..a20e9b3 100644 --- a/src/server/sessiond/config.ts +++ b/src/server/sessiond/config.ts @@ -1,8 +1,8 @@ -import { homedir } from "node:os"; import { join } from "node:path"; +import { piWebDataDir } from "../../config.js"; export function sessiondSocketPath(): string { - return process.env["PI_WEB_SESSIOND_SOCKET"] ?? join(homedir(), ".pi-web", "sessiond.sock"); + return process.env["PI_WEB_SESSIOND_SOCKET"] ?? join(piWebDataDir(), "sessiond.sock"); } export function sessiondHttpUrl(): string | undefined { diff --git a/src/server/storage/projectStore.test.ts b/src/server/storage/projectStore.test.ts new file mode 100644 index 0000000..9a7bbbb --- /dev/null +++ b/src/server/storage/projectStore.test.ts @@ -0,0 +1,13 @@ +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { projectStorePath } from "./projectStore.js"; + +describe("projectStorePath", () => { + it("uses PI_WEB_DATA_DIR by default", () => { + expect(projectStorePath({ PI_WEB_DATA_DIR: "demo-data" }, "/tmp/pi-web")).toBe(join("/tmp/pi-web", "demo-data", "projects.json")); + }); + + it("uses PI_WEB_PROJECTS_FILE when configured", () => { + expect(projectStorePath({ PI_WEB_PROJECTS_FILE: "demo/projects.json" }, "/tmp/pi-web")).toBe(join("/tmp/pi-web", "demo/projects.json")); + }); +}); diff --git a/src/server/storage/projectStore.ts b/src/server/storage/projectStore.ts index 890585c..c61ee51 100644 --- a/src/server/storage/projectStore.ts +++ b/src/server/storage/projectStore.ts @@ -1,6 +1,6 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, join } from "node:path"; -import { homedir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import { piWebDataDir } from "../../config.js"; import { randomUUID } from "node:crypto"; import type { Project } from "../types.js"; @@ -31,8 +31,18 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null; } +export function defaultProjectStorePath(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): string { + return join(piWebDataDir(env, cwd), "projects.json"); +} + +export function projectStorePath(env: NodeJS.ProcessEnv = process.env, cwd = process.cwd()): string { + const configured = env["PI_WEB_PROJECTS_FILE"]; + if (configured === undefined || configured === "") return defaultProjectStorePath(env, cwd); + return resolve(cwd, configured); +} + export class ProjectStore { - constructor(private readonly filePath = join(homedir(), ".pi-web", "projects.json")) {} + constructor(private readonly filePath = projectStorePath()) {} async list(): Promise { return (await this.read()).projects;