import { createRoute } from "../../../../modules/api-utils/createRoute"; import { unifiSites } from "../../../../managers/unifiSites"; import { apiResponse } from "../../../../modules/api-utils/apiResponse"; import { ContentfulStatusCode } from "hono/utils/http-status"; import { authMiddleware } from "../../../middleware/authorization"; import { toWlanConfUpdate } from "../../../../modules/unifi-api/unifiTypes"; import { z } from "zod"; /* PATCH /v1/unifi/site/:id/wifi/:wlanId */ export default createRoute( "patch", ["/site/:id/wifi/:wlanId"], async (c) => { const site = await unifiSites.fetch(c.req.param("id")); const wlanId = c.req.param("wlanId"); const body = await c.req.json(); const schema = z .object({ name: z.string().optional(), passphrase: z.string().optional(), enabled: z.boolean().optional(), security: z.enum(["wpapsk", "wpaeap", "open", "osen"]).optional(), wpaMode: z.enum(["wpa2", "wpa3", "wpa2wpa3"]).optional(), wpaEnc: z.enum(["ccmp", "gcmp", "ccmp-gcmp"]).optional(), hideSSID: z.boolean().optional(), macFilterEnabled: z.boolean().optional(), macFilterPolicy: z.enum(["allow", "deny"]).optional(), isGuest: z.boolean().optional(), l2Isolation: z.boolean().optional(), fastRoamingEnabled: z.boolean().optional(), bssTransition: z.boolean().optional(), uapsdEnabled: z.boolean().optional(), groupRekey: z.number().optional(), dtimMode: z.enum(["default", "custom"]).optional(), dtimNg: z.number().optional(), dtimNa: z.number().optional(), minrateNgEnabled: z.boolean().optional(), minrateNaEnabled: z.boolean().optional(), radiusDasEnabled: z.boolean().optional(), radiusMacAuthEnabled: z.boolean().optional(), pmfMode: z.enum(["disabled", "optional", "required"]).optional(), band: z.enum(["both", "2g", "5g"]).optional(), usergroupId: z.string().optional(), proxyArp: z.boolean().optional(), mcastenhanceEnabled: z.boolean().optional(), macFilterList: z.array(z.string()).optional(), no2ghzOui: z.boolean().optional(), apGroupIds: z.array(z.string()).optional(), apGroupMode: z.enum(["all", "groups", "devices"]).optional(), deviceMacs: z.array(z.string()).optional(), }) .strict(); const parsed = schema.parse(body); // If deviceMacs is provided, manage the devices_ap_group: // - If already in devices mode with a for_wlanconf group, update that group // - Otherwise create a new for_wlanconf group and switch to devices mode if (parsed.deviceMacs && parsed.deviceMacs.length > 0) { const wlans = await unifiSites.getWlanConf(site.siteId); const currentWlan = wlans.find((w) => w.id === wlanId); let existingGroupId: string | undefined; if ( currentWlan?.apGroupMode === "devices" && currentWlan.apGroupIds.length > 0 ) { // Check if the current group is a for_wlanconf group we can update const apGroups = await unifiSites.getApGroups(site.siteId); const currentGroup = apGroups.find( (g) => g.id === currentWlan.apGroupIds[0] && g.forWlanconf, ); if (currentGroup) existingGroupId = currentGroup.id; } if (existingGroupId) { // Update the existing for_wlanconf group's device list await unifiSites.updateApGroup( site.siteId, existingGroupId, parsed.deviceMacs, ); parsed.apGroupMode = "devices"; parsed.apGroupIds = [existingGroupId]; } else { // Create a new for_wlanconf group const apGroup = await unifiSites.createApGroup( site.siteId, "devices_ap_group", parsed.deviceMacs, true, // for_wlanconf must be true for devices mode ); parsed.apGroupMode = "devices"; parsed.apGroupIds = [apGroup.id]; } } // Remove deviceMacs before converting — it's not a UniFi field const { deviceMacs: _, ...updateInput } = parsed; const updates = toWlanConfUpdate(updateInput); const result = await unifiSites.updateWlanConf( site.siteId, wlanId, updates, ); const response = apiResponse.successful( "UniFi WiFi Network Updated Successfully!", result, ); return c.json(response, response.status as ContentfulStatusCode); }, authMiddleware({ permissions: ["unifi.access", "unifi.site.wifi", "unifi.site.wifi.update"], }), );