Setup unifi wlans

This commit is contained in:
2026-02-22 19:12:13 -06:00
parent a99c9f5102
commit 6791a6735b
38 changed files with 24435 additions and 1663 deletions
+2
View File
@@ -11,6 +11,8 @@ export const optima = {
role: (await import("./optima-api/modules/roles")).role,
permission: (await import("./optima-api/modules/permissions")).permission,
user,
users: (await import("./optima-api/modules/users")).users,
unifi: (await import("./optima-api/modules/unifi")).unifi,
};
/**
* @TODO
+7 -1
View File
@@ -4,10 +4,16 @@ export const company = {
async fetch(
accessToken: string,
id: string,
options?: { includeAddress?: boolean },
options?: {
includeAddress?: boolean;
includePrimaryContact?: boolean;
includeAllContacts?: boolean;
},
) {
const params: Record<string, string> = {};
if (options?.includeAddress) params.includeAddress = "true";
if (options?.includePrimaryContact) params.includePrimaryContact = "true";
if (options?.includeAllContacts) params.includeAllContacts = "true";
const company = await api.get(`/v1/company/companies/${id}`, {
params,
@@ -5,7 +5,8 @@ export interface CredentialTypeField {
name: string;
required: boolean;
secure: boolean;
valueType: "plain_text" | "password" | "number" | "email" | "url";
valueType: string;
subFields?: CredentialTypeField[];
}
export interface CredentialType {
+87 -2
View File
@@ -2,15 +2,20 @@ import api from "../axios";
export interface CredentialField {
id: string;
fieldId: string;
name: string;
secure: boolean;
required: boolean;
valueType: string;
value: string;
}
export interface Credential {
id: string;
name: string;
notes?: string;
typeId: string;
companyId: string;
subCredentialOfId?: string;
fields: CredentialField[];
type?: {
id: string;
@@ -45,6 +50,7 @@ export const credential = {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
@@ -62,7 +68,11 @@ export const credential = {
return response.data;
},
async update(accessToken: string, id: string, data: { name: string }) {
async update(
accessToken: string,
id: string,
data: { name?: string; notes?: string },
) {
const response = await api.patch(`/v1/credential/credentials/${id}`, data, {
headers: {
Authorization: `Bearer ${accessToken}`,
@@ -96,4 +106,79 @@ export const credential = {
});
return response.data;
},
async fetchSecureValue(
accessToken: string,
credentialId: string,
fieldId: string,
) {
const response = await api.get(
`/v1/credential/credentials/${credentialId}/secure-values/${fieldId}`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return response.data;
},
async fetchValueTypes(accessToken: string) {
const response = await api.get("/v1/credential/valuetypes", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
async fetchSubCredentials(accessToken: string, credentialId: string) {
const response = await api.get(
`/v1/credential/credentials/${credentialId}/sub-credentials`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return response.data;
},
async addSubCredential(
accessToken: string,
credentialId: string,
data: {
fieldId: string;
name: string;
fields: Array<{ fieldId: string; value: string }>;
},
) {
const response = await api.post(
`/v1/credential/credentials/${credentialId}/sub-credentials`,
data,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return response.data;
},
async removeSubCredential(
accessToken: string,
credentialId: string,
subId: string,
) {
const response = await api.delete(
`/v1/credential/credentials/${credentialId}/sub-credentials/${subId}`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return response.data;
},
};
+383
View File
@@ -0,0 +1,383 @@
import api from "../axios";
export interface UnifiSite {
id: string;
name: string;
siteId: string;
companyId: string | null;
company?: {
id: string;
name: string;
};
createdAt: string;
updatedAt: string;
}
export interface UnifiSiteOverview {
health: Array<{
subsystem: string;
status: string;
numAdopted?: number;
numGateway?: number;
[key: string]: unknown;
}>;
sysInfo: {
timezone?: string;
hostname?: string;
version?: string;
[key: string]: unknown;
};
siteInfo: {
description?: string;
name?: string;
[key: string]: unknown;
};
}
export interface UnifiDevice {
id: string;
mac: string;
model: string;
name: string;
type: string;
state: string | number;
ip: string;
version: string;
uptime: number;
radios?: unknown[];
uplink?: unknown;
[key: string]: unknown;
}
export interface UnifiWifiNetwork {
id: string;
name?: string;
siteId?: string;
enabled?: boolean;
security?: string;
wpaMode?: string;
wpaEnc?: string;
wpa3Support?: boolean;
wpa3Transition?: boolean;
wpa3FastRoaming?: boolean;
wpa3Enhanced192?: boolean;
passphrase?: string;
passphraseAutogenerated?: boolean;
hideSSID?: boolean;
isGuest?: boolean;
band?: string;
bands?: string[];
networkconfId?: string;
usergroupId?: string;
apGroupIds?: string[];
apGroupMode?: string;
pmfMode?: string;
groupRekey?: number;
dtimMode?: string;
dtimNg?: number;
dtimNa?: number;
dtim6e?: number;
l2Isolation?: boolean;
fastRoamingEnabled?: boolean;
bssTransition?: boolean;
uapsdEnabled?: boolean;
iappEnabled?: boolean;
proxyArp?: boolean;
mcastenhanceEnabled?: boolean;
macFilterEnabled?: boolean;
macFilterPolicy?: string;
macFilterList?: string[];
radiusDasEnabled?: boolean;
radiusMacAuthEnabled?: boolean;
radiusMacaclFormat?: string;
minrateSettingPreference?: string;
minrateNgEnabled?: boolean;
minrateNgDataRateKbps?: number;
minrateNgAdvertisingRates?: boolean;
minrateNaEnabled?: boolean;
minrateNaDataRateKbps?: number;
minrateNaAdvertisingRates?: boolean;
settingPreference?: string;
no2ghzOui?: boolean;
privatePreSharedKeysEnabled?: boolean;
privatePreSharedKeys?: unknown[];
saeGroups?: unknown[];
saePsk?: unknown[];
schedule?: unknown[];
scheduleWithDuration?: unknown[];
bcFilterList?: unknown[];
externalId?: string;
[key: string]: unknown;
}
export interface UnifiNetwork {
id: string;
name: string;
purpose: string;
subnet: string;
vlanId: number | null;
dhcpEnabled: boolean;
dhcpStart: string;
dhcpStop: string;
domainName: string;
isNat: boolean;
enabled: boolean;
}
export interface UnifiWlanGroup {
id: string;
name: string;
siteId: string;
noDelete: boolean;
noEdit: boolean;
hidden: boolean;
}
export interface UnifiApGroup {
id: string;
name: string;
deviceMacs: string[];
noDelete: boolean;
}
export interface UnifiAccessPoint {
id: string;
mac: string;
model: string;
type: string;
name: string;
state: number;
adopted: boolean;
ip: string;
version: string;
}
export interface UnifiSpeedProfile {
id: string;
name: string;
siteId: string;
noDelete: boolean;
downloadLimitKbps: number;
uploadLimitKbps: number;
}
export interface UnifiPPSK {
key: string;
name: string;
mac: string | null;
vlanId: number | null;
}
export const unifi = {
/** Fetch all UniFi sites */
async fetchSites(accessToken: string) {
const response = await api.get("/v1/unifi/sites", {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Sync sites from UniFi controller */
async syncSites(accessToken: string) {
const response = await api.post(
"/v1/unifi/sites/sync",
{},
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Create a new UniFi site */
async createSite(accessToken: string, description: string) {
const response = await api.post(
"/v1/unifi/sites/create",
{ description },
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Fetch a single UniFi site */
async fetchSite(accessToken: string, id: string) {
const response = await api.get(`/v1/unifi/site/${id}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Fetch UniFi sites linked to a company */
async fetchCompanySites(accessToken: string, companyId: string) {
const response = await api.get(
`/v1/company/companies/${companyId}/unifi/sites`,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Link a site to a company */
async linkSite(accessToken: string, siteId: string, companyId: string) {
const response = await api.post(
`/v1/unifi/site/${siteId}/link`,
{ companyId },
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Unlink a site from its company */
async unlinkSite(accessToken: string, siteId: string) {
const response = await api.post(
`/v1/unifi/site/${siteId}/unlink`,
{},
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Get site overview (health, sysInfo, siteInfo) */
async fetchSiteOverview(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/overview`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get site devices */
async fetchSiteDevices(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/devices`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get site WiFi networks */
async fetchSiteWifi(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/wifi`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Update a WiFi network */
async updateWifi(
accessToken: string,
siteId: string,
wlanId: string,
data: Record<string, unknown>,
) {
const response = await api.patch(
`/v1/unifi/site/${siteId}/wifi/${wlanId}`,
data,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Get site networks */
async fetchSiteNetworks(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/networks`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get WLAN groups */
async fetchWlanGroups(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/wlan-groups`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get AP groups (collections of access points for broadcasting) */
async fetchApGroups(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/ap-groups`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get access points */
async fetchAccessPoints(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/access-points`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Get speed profiles (user groups) */
async fetchSpeedProfiles(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/speed-profiles`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
/** Create a speed profile */
async createSpeedProfile(
accessToken: string,
siteId: string,
data: {
name: string;
downloadLimitKbps?: number;
uploadLimitKbps?: number;
},
) {
const response = await api.post(
`/v1/unifi/site/${siteId}/speed-profiles`,
data,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Get private PSKs for a WLAN */
async fetchPPSKs(accessToken: string, siteId: string, wlanId: string) {
const response = await api.get(
`/v1/unifi/site/${siteId}/wifi/${wlanId}/ppsk`,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Create a private PSK on a WLAN */
async createPPSK(
accessToken: string,
siteId: string,
wlanId: string,
data: { key: string; name: string; mac?: string; vlanId?: number },
) {
const response = await api.post(
`/v1/unifi/site/${siteId}/wifi/${wlanId}/ppsk`,
data,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return response.data;
},
/** Get WiFi limits per AP per radio */
async fetchWifiLimits(accessToken: string, siteId: string) {
const response = await api.get(`/v1/unifi/site/${siteId}/wifi-limits`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
return response.data;
},
};
+2 -2
View File
@@ -89,8 +89,8 @@ export const user = {
} catch {}
reject(new Error("Timed out waiting for auth callback"));
},
2 * 60 * 1000,
); // 2 minutes
5 * 60 * 1000,
); // 5 minutes
const handlePayload = (payload: any) => {
try {
+126
View File
@@ -0,0 +1,126 @@
import api from "../axios";
import type { Role } from "./roles";
export interface User {
id: string;
name: string;
email: string;
login: string;
image?: string;
roles: string[];
permissions?: string[];
createdAt: string;
updatedAt: string;
}
export interface PermissionCheckResult {
permission: string;
hasPermission: boolean;
}
export const users = {
/**
* Fetch all users.
* Requires: user.read.other, user.list.other
*/
async fetchAll(accessToken: string): Promise<{ data: User[] }> {
const response = await api.get("/v1/user/users", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
/**
* Fetch a specific user by their ID.
* Requires: user.read.other
*/
async fetch(
accessToken: string,
identifier: string,
): Promise<{ data: User }> {
const response = await api.get(`/v1/user/users/${identifier}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
/**
* Update a specific user's information.
* Requires: user.write.other
* Conditional: user.roles.other (if roles included), user.permissions.other (if permissions included)
*/
async update(
accessToken: string,
identifier: string,
updates: {
name?: string;
image?: string;
roles?: string[];
permissions?: string[];
},
): Promise<{ data: User }> {
const response = await api.patch(`/v1/user/users/${identifier}`, updates, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
/**
* Delete a specific user.
* Requires: user.delete.other
*/
async delete(
accessToken: string,
identifier: string,
): Promise<{ data: User }> {
const response = await api.delete(`/v1/user/users/${identifier}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
/**
* Fetch all roles assigned to a specific user.
* Requires: user.read.other, role.read
*/
async fetchRoles(
accessToken: string,
identifier: string,
): Promise<{ data: Role[] }> {
const response = await api.get(`/v1/user/users/${identifier}/roles`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
},
/**
* Check if a specific user has certain permissions.
* Requires: user.read.other
*/
async checkPermissions(
accessToken: string,
identifier: string,
permissions: string[],
): Promise<{ data: { results: PermissionCheckResult[] } }> {
const response = await api.post(
`/v1/user/users/${identifier}/check-permission`,
{ permissions },
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return response.data;
},
};