mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Probe the selected WiFi device with iw to determine which hotspot protocols it can host: WPA2 requires AP mode; WPA3 requires SAE in AP mode (SAE_OFFLOAD_AP, or a mac80211 driver with the CMAC cipher for software SAE). The Raspberry Pi's brcmfmac supports neither, so Pis correctly offer WPA2 only (verified against real Pi 4/Pi 5 iw output). The protocol is saved as hotspot.protocol in controller.yaml. enable_hotspot() now always sets the Hotspot connection's key-mgmt explicitly (sae + required PMF for WPA3) so a reused profile cannot keep a stale protocol, and reverts to WPA2 if the driver rejects SAE. New GET /api/hotspot/protocols endpoint; protocol dropdowns added to both the ControllerPage React UI and the fallback controller UI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
257 lines
6.9 KiB
JavaScript
257 lines
6.9 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 enableHotspot() {
|
|
return controllerFetch('/hotspot/enable', {method: 'POST'});
|
|
}
|
|
|
|
export async function disableHotspot() {
|
|
return controllerFetch('/hotspot/disable', {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 enableBluetooth() {
|
|
return controllerFetch('/bluetooth/enable', {method: 'POST'});
|
|
}
|
|
|
|
export async function disableBluetooth() {
|
|
return controllerFetch('/bluetooth/disable', {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');
|
|
}
|