mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Add a VNC toggle to the Controller (both UIs) that starts/stops whichever VNC backend is installed: wayvnc for Wayland (Raspberry Pi OS), RealVNC for X11. Runtime-only, like the Desktop toggle: the unit is never enabled, so VNC does not return after a reboot. Users who want it permanently on can enable it with raspi-config. VNC serves the desktop session, so starting it without a desktop gives clients a blank screen. The gate lives in the backend, not just the UI, so curl callers get it too (409, distinct from a systemctl failure). Stopping is never gated so a running VNC server can't be stranded on with no way to turn it off. Backend selection deliberately avoids raspi-config's live-compositor probe (pgrep labwc), which reports X11 — and would pick the wrong backend — whenever the desktop is stopped. wayvnc also requires its config to exist; that is checked directly rather than via the unit's ConditionResult, which reports 'no' for any unit that has never been started. Extracts _unit_properties()/_systemctl_unit() so Desktop and VNC share one systemd probe and start/stop path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
281 lines
7.4 KiB
JavaScript
281 lines
7.4 KiB
JavaScript
/**
|
|
* Controller API client for WROLPi.
|
|
*
|
|
* These functions call the Controller service endpoints.
|
|
* Controller handles system-level operations that need to work
|
|
* even when the main API is down.
|
|
*/
|
|
|
|
// Controller endpoints are served under /controller/api
|
|
const CONTROLLER_BASE = '/controller/api';
|
|
|
|
/**
|
|
* Fetch with error handling for Controller endpoints.
|
|
*/
|
|
async function controllerFetch(endpoint, options = {}) {
|
|
const url = `${CONTROLLER_BASE}${endpoint}`;
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({detail: response.statusText}));
|
|
throw new Error(error.detail || 'Controller request failed');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// --- Status Endpoints ---
|
|
|
|
/**
|
|
* Get all system stats from the Controller.
|
|
* Returns CPU, memory, load, drives, network, power, iostat, processes,
|
|
* disk_bandwidth along with system info (dockerized, is_rpi, hotspot, throttle).
|
|
*/
|
|
export async function getControllerStats() {
|
|
return controllerFetch('/stats');
|
|
}
|
|
|
|
export async function getCpuStatus() {
|
|
return controllerFetch('/status/cpu');
|
|
}
|
|
|
|
export async function getMemoryStatus() {
|
|
return controllerFetch('/status/memory');
|
|
}
|
|
|
|
export async function getLoadStatus() {
|
|
return controllerFetch('/status/load');
|
|
}
|
|
|
|
export async function getDrivesStatus() {
|
|
return controllerFetch('/status/drives');
|
|
}
|
|
|
|
export async function getPrimaryDriveStatus() {
|
|
return controllerFetch('/status/primary-drive');
|
|
}
|
|
|
|
export async function getNetworkStatus() {
|
|
return controllerFetch('/status/network');
|
|
}
|
|
|
|
export async function getPowerStatus() {
|
|
return controllerFetch('/status/power');
|
|
}
|
|
|
|
// --- Admin Endpoints ---
|
|
|
|
export async function getHotspotStatus() {
|
|
return controllerFetch('/hotspot/status');
|
|
}
|
|
|
|
export async function getHotspotDevices() {
|
|
return controllerFetch('/hotspot/devices');
|
|
}
|
|
|
|
export async function getHotspotProtocols(device) {
|
|
const query = device ? `?device=${encodeURIComponent(device)}` : '';
|
|
return controllerFetch(`/hotspot/protocols${query}`);
|
|
}
|
|
|
|
export async function getHotspotSettings() {
|
|
return controllerFetch('/hotspot/settings');
|
|
}
|
|
|
|
export async function updateHotspotSettings(settings) {
|
|
return controllerFetch('/hotspot/settings', {method: 'POST', body: JSON.stringify(settings)});
|
|
}
|
|
|
|
export async function startHotspot() {
|
|
return controllerFetch('/hotspot/start', {method: 'POST'});
|
|
}
|
|
|
|
export async function stopHotspot() {
|
|
return controllerFetch('/hotspot/stop', {method: 'POST'});
|
|
}
|
|
|
|
export async function getThrottleStatus() {
|
|
return controllerFetch('/throttle/status');
|
|
}
|
|
|
|
export async function enableThrottle() {
|
|
return controllerFetch('/throttle/enable', {method: 'POST'});
|
|
}
|
|
|
|
export async function disableThrottle() {
|
|
return controllerFetch('/throttle/disable', {method: 'POST'});
|
|
}
|
|
|
|
export async function getBluetoothStatus() {
|
|
return controllerFetch('/bluetooth/status');
|
|
}
|
|
|
|
export async function unblockBluetooth() {
|
|
return controllerFetch('/bluetooth/unblock', {method: 'POST'});
|
|
}
|
|
|
|
export async function blockBluetooth() {
|
|
return controllerFetch('/bluetooth/block', {method: 'POST'});
|
|
}
|
|
|
|
export async function getDesktopStatus() {
|
|
return controllerFetch('/desktop/status');
|
|
}
|
|
|
|
export async function startDesktop() {
|
|
return controllerFetch('/desktop/start', {method: 'POST'});
|
|
}
|
|
|
|
export async function stopDesktop() {
|
|
return controllerFetch('/desktop/stop', {method: 'POST'});
|
|
}
|
|
|
|
export async function getVncStatus() {
|
|
return controllerFetch('/vnc/status');
|
|
}
|
|
|
|
export async function startVnc() {
|
|
return controllerFetch('/vnc/start', {method: 'POST'});
|
|
}
|
|
|
|
export async function stopVnc() {
|
|
return controllerFetch('/vnc/stop', {method: 'POST'});
|
|
}
|
|
|
|
// --- Samba Endpoints ---
|
|
|
|
export async function getSambaStatus() {
|
|
return controllerFetch('/samba/status');
|
|
}
|
|
|
|
export async function addSambaShare(name, path, readOnly = true, comment = '') {
|
|
return controllerFetch('/samba/shares', {
|
|
method: 'POST',
|
|
body: JSON.stringify({name, path, read_only: readOnly, comment}),
|
|
});
|
|
}
|
|
|
|
export async function removeSambaShare(shareName) {
|
|
return controllerFetch(`/samba/shares/${encodeURIComponent(shareName)}`, {method: 'DELETE'});
|
|
}
|
|
|
|
export async function shutdownSystem() {
|
|
return controllerFetch('/shutdown', {method: 'POST'});
|
|
}
|
|
|
|
export async function rebootSystem() {
|
|
return controllerFetch('/reboot', {method: 'POST'});
|
|
}
|
|
|
|
export async function restartServices() {
|
|
return controllerFetch('/restart', {method: 'POST'});
|
|
}
|
|
|
|
// --- Service Endpoints ---
|
|
|
|
export async function getServices() {
|
|
return controllerFetch('/services');
|
|
}
|
|
|
|
export async function getServiceStatus(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}`);
|
|
}
|
|
|
|
export async function startService(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}/start`, {method: 'POST'});
|
|
}
|
|
|
|
export async function stopService(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}/stop`, {method: 'POST'});
|
|
}
|
|
|
|
export async function restartService(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}/restart`, {method: 'POST'});
|
|
}
|
|
|
|
export async function enableService(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}/enable`, {method: 'POST'});
|
|
}
|
|
|
|
export async function disableService(serviceName) {
|
|
return controllerFetch(`/services/${serviceName}/disable`, {method: 'POST'});
|
|
}
|
|
|
|
export async function getServiceLogs(serviceName, lines = 100) {
|
|
return controllerFetch(`/services/${serviceName}/logs?lines=${lines}`);
|
|
}
|
|
|
|
// --- Disk Endpoints ---
|
|
|
|
export async function getDisks() {
|
|
return controllerFetch('/disks');
|
|
}
|
|
|
|
export async function getMounts() {
|
|
return controllerFetch('/disks/mounts');
|
|
}
|
|
|
|
export async function mountDisk(device, mountPoint, fstype = null, options = 'defaults', persist = false, forceShadowed = false) {
|
|
return controllerFetch('/disks/mount', {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
device,
|
|
mount_point: mountPoint,
|
|
fstype,
|
|
options,
|
|
persist,
|
|
force_shadowed: forceShadowed,
|
|
}),
|
|
});
|
|
}
|
|
|
|
export async function unmountDisk(mountPoint, lazy = false) {
|
|
return controllerFetch('/disks/unmount', {
|
|
method: 'POST',
|
|
body: JSON.stringify({mount_point: mountPoint, lazy}),
|
|
});
|
|
}
|
|
|
|
export async function detectWrolpiDrives() {
|
|
return controllerFetch('/disks/detect-wrolpi');
|
|
}
|
|
|
|
export async function getSmartStatus() {
|
|
return controllerFetch('/disks/smart');
|
|
}
|
|
|
|
// --- Fstab Endpoints ---
|
|
|
|
export async function getFstabEntries() {
|
|
return controllerFetch('/disks/fstab');
|
|
}
|
|
|
|
export async function addFstabEntry(device, mountPoint, fstype, options = 'defaults,nofail,x-systemd.device-timeout=10s') {
|
|
return controllerFetch('/disks/fstab', {
|
|
method: 'POST',
|
|
body: JSON.stringify({device, mount_point: mountPoint, fstype, options}),
|
|
});
|
|
}
|
|
|
|
export async function removeFstabEntry(mountPoint) {
|
|
const encodedPath = encodeURIComponent(mountPoint.slice(1));
|
|
return controllerFetch(`/disks/fstab/${encodedPath}`, {method: 'DELETE'});
|
|
}
|
|
|
|
// --- Health Check ---
|
|
|
|
export async function healthCheck() {
|
|
return controllerFetch('/health');
|
|
}
|
|
|
|
// --- Controller Info ---
|
|
|
|
export async function getControllerInfo() {
|
|
return controllerFetch('/info');
|
|
}
|