feat: finish the host-metrics plugin, move host status into core (B16)

This commit is contained in:
LukeGus committed 2026-09-24 11:21:23 -05:00
1 parent 14eb558b85
commit 0a3f05d99d
308 files changed
+53073 -8985

No files matched your search

+1
View File
@@ -15,6 +15,7 @@
"api": "1"
},
"capabilities": [
"hosts:read",
"settings:read-core",
"network:outbound",
"network:serve",
@@ -1,15 +1,15 @@
import type { Router } from "express";
import type { Request, Response } from "express";
import type { PluginContext } from "@termix/plugin-sdk/backend";
import type { AuthenticatedRequest } from "../../../../src/types/index.js";
import { DataCrypto } from "../../../../src/backend/utils/data-crypto.js";
import { PermissionManager } from "../../../../src/backend/utils/permission-manager.js";
import { resolveHostById } from "../../../../src/backend/hosts/host-resolver.js";
import { execCommand } from "../../../../src/backend/hosts/metrics-shared/common-utils.js";
import { execElevated } from "../../../../src/backend/hosts/metrics-shared/exec-elevated.js";
import { isValidTailscaleAction } from "../../../../src/backend/hosts/metrics-shared/validation.js";
import { execCommand, execElevated } from "@termix/plugin-sdk/host-commands";
import { withSshConnection } from "./ssh.js";
export type TailscaleAction = "up" | "down";
export function isValidTailscaleAction(a: unknown): a is TailscaleAction {
return a === "up" || a === "down";
}
export interface TailscalePeer {
hostname: string;
tailscaleIPs: string[];
@@ -116,32 +116,29 @@ interface ManagerHost {
sudoPassword?: string;
}
/**
* The host as the acting user may reach it. The resolved host carries the
* owner's sudo password even for a shared user: core decrypts it with the
* owner's key.
*/
async function resolveManagerHost(
ctx: PluginContext,
hostId: number,
userId: string,
): Promise<ManagerHost> {
const access = await PermissionManager.getInstance().canAccessHost(
userId,
hostId,
"connect",
);
const access = await ctx.hosts.checkAccess(hostId, "connect");
if (!access.hasAccess) throw new AccessDeniedError();
if (DataCrypto.getUserDataKey(userId) === null) {
throw new AccessDeniedError();
}
const host = await resolveHostById(hostId, userId);
const host = await ctx.ssh.resolveHost(hostId);
if (!host) throw new AccessDeniedError("Host not found");
let sudoPassword = (host as { sudoPassword?: string }).sudoPassword;
const ownerId = (host as { userId?: string }).userId;
if (ownerId && ownerId !== userId) {
const ownerHost = await resolveHostById(hostId, ownerId);
sudoPassword = (ownerHost as { sudoPassword?: string } | null)
?.sudoPassword;
}
return { id: hostId, userId, sudoPassword };
const terminalConfig = host.terminalConfig as
{ sudoPassword?: string } | undefined;
return {
id: hostId,
userId: String(host.userId ?? ""),
sudoPassword:
(host.sudoPassword as string | undefined) ||
terminalConfig?.sudoPassword ||
undefined,
};
}
function managerErrorResponse(
@@ -183,10 +180,9 @@ export function registerTailscaleHostMetricsManager(
router.get(
"/host-metrics-manager/:id",
async (req: Request, res: Response) => {
const userId = (req as AuthenticatedRequest).userId;
const hostId = parseInt(String(req.params.id), 10);
try {
const host = await resolveManagerHost(hostId, userId);
const host = await resolveManagerHost(ctx, hostId);
const data = await withSshConnection(
host.id,
{ pool: "tailscale", purpose: "metrics" },
@@ -236,10 +232,9 @@ export function registerTailscaleHostMetricsManager(
router.post(
"/host-metrics-manager/:id/action",
async (req: Request, res: Response) => {
const userId = (req as AuthenticatedRequest).userId;
const hostId = parseInt(String(req.params.id), 10);
try {
const host = await resolveManagerHost(hostId, userId);
const host = await resolveManagerHost(ctx, hostId);
const { action } = req.body as { action: unknown };
if (!isValidTailscaleAction(action)) {
throw new ManagerInputError("Invalid action, must be 'up' or 'down'");