Add nasctl: Go NAS control plane with React frontend
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
export interface SambaShare {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
comment: string;
|
||||
read_only: boolean;
|
||||
guest_ok: boolean;
|
||||
valid_users: string[];
|
||||
valid_groups: string[];
|
||||
}
|
||||
|
||||
export interface NFSExport {
|
||||
id: number;
|
||||
path: string;
|
||||
clients: string[];
|
||||
options: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
groups: string[];
|
||||
smb_enabled: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface DirtyModule {
|
||||
module: string;
|
||||
marked_at: string;
|
||||
}
|
||||
|
||||
export interface ApplyLogEntry {
|
||||
id: number;
|
||||
module: string;
|
||||
message: string;
|
||||
success: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface DiskUsage {
|
||||
path: string;
|
||||
total_bytes: number;
|
||||
free_bytes: number;
|
||||
used_bytes: number;
|
||||
used_percent: number;
|
||||
}
|
||||
|
||||
export interface ServiceStatus {
|
||||
name: string;
|
||||
active: boolean;
|
||||
state: string;
|
||||
}
|
||||
|
||||
export interface SystemStatus {
|
||||
disks: DiskUsage[];
|
||||
services: ServiceStatus[];
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
constructor(status: number, message: string) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||||
const res = await fetch(`/api${path}`, {
|
||||
method,
|
||||
headers: body ? { "Content-Type": "application/json" } : undefined,
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
if (res.status === 204) {
|
||||
return undefined as T;
|
||||
}
|
||||
const text = await res.text();
|
||||
const data = text ? JSON.parse(text) : {};
|
||||
if (!res.ok) {
|
||||
throw new ApiError(res.status, data.error || res.statusText);
|
||||
}
|
||||
return data as T;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
// auth
|
||||
status: () => request<{ authenticated: boolean; username: string }>("GET", "/auth/status"),
|
||||
login: (username: string, password: string) =>
|
||||
request<{ username: string }>("POST", "/auth/login", { username, password }),
|
||||
logout: () => request<{ ok: boolean }>("POST", "/auth/logout"),
|
||||
|
||||
// dirty / apply
|
||||
dirty: () => request<{ modules: DirtyModule[] | null }>("GET", "/dirty"),
|
||||
apply: () => request<{ results: { module: string; applied: boolean; error?: string }[] }>("POST", "/apply"),
|
||||
applyLog: () => request<{ entries: ApplyLogEntry[] }>("GET", "/apply/log"),
|
||||
systemStatus: () => request<SystemStatus>("GET", "/system/status"),
|
||||
|
||||
// samba
|
||||
listShares: () => request<{ shares: SambaShare[] | null }>("GET", "/samba/shares/"),
|
||||
createShare: (s: Partial<SambaShare>) => request<SambaShare>("POST", "/samba/shares/", s),
|
||||
updateShare: (id: number, s: Partial<SambaShare>) => request<SambaShare>("PUT", `/samba/shares/${id}/`, s),
|
||||
deleteShare: (id: number) => request<void>("DELETE", `/samba/shares/${id}/`),
|
||||
|
||||
// nfs
|
||||
listExports: () => request<{ exports: NFSExport[] | null }>("GET", "/nfs/exports/"),
|
||||
createExport: (e: Partial<NFSExport>) => request<NFSExport>("POST", "/nfs/exports/", e),
|
||||
updateExport: (id: number, e: Partial<NFSExport>) => request<NFSExport>("PUT", `/nfs/exports/${id}/`, e),
|
||||
deleteExport: (id: number) => request<void>("DELETE", `/nfs/exports/${id}/`),
|
||||
|
||||
// users
|
||||
listUsers: () => request<{ users: User[] | null }>("GET", "/users/"),
|
||||
createUser: (u: Partial<User> & { password?: string }) => request<User>("POST", "/users/", u),
|
||||
updateUser: (id: number, u: Partial<User> & { password?: string }) => request<User>("PUT", `/users/${id}/`, u),
|
||||
deleteUser: (id: number) => request<void>("DELETE", `/users/${id}/`),
|
||||
};
|
||||
|
||||
export function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return "0 B";
|
||||
const units = ["B", "KB", "MB", "GB", "TB", "PB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
|
||||
}
|
||||
Reference in New Issue
Block a user