124 lines
3.2 KiB
TypeScript
124 lines
3.2 KiB
TypeScript
// unifi-wifi-list.ts
|
|
import axios, { AxiosInstance } from "axios";
|
|
|
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
|
|
const controllerBaseUrl = "https://unifi.totaltech.net";
|
|
const site = "km9b1v8i";
|
|
const username = "admin";
|
|
const password = "Tt$Un1fiIZth3B3$t26";
|
|
|
|
interface WlanConfRaw {
|
|
_id: string;
|
|
name?: string;
|
|
ssid?: string;
|
|
x_passphrase?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
interface WlanConf {
|
|
id: string;
|
|
ssid: string;
|
|
password: string | null;
|
|
}
|
|
|
|
class UnifiClient {
|
|
private client: AxiosInstance;
|
|
|
|
constructor(baseURL: string) {
|
|
this.client = axios.create({
|
|
baseURL,
|
|
validateStatus: (s) => s >= 200 && s < 400,
|
|
});
|
|
}
|
|
|
|
private persistSession(res: { headers: Record<string, unknown> }): void {
|
|
// Cookies
|
|
const raw = res.headers["set-cookie"];
|
|
if (raw) {
|
|
const cookies = (Array.isArray(raw) ? raw : [raw]) as string[];
|
|
const cookieString = cookies.map((c) => c.split(";")[0]).join("; ");
|
|
this.client.defaults.headers.common["Cookie"] = cookieString;
|
|
}
|
|
// CSRF token (UniFi OS)
|
|
const csrf = res.headers["x-csrf-token"];
|
|
if (typeof csrf === "string") {
|
|
this.client.defaults.headers.common["X-CSRF-Token"] = csrf;
|
|
}
|
|
}
|
|
|
|
async login(username: string, password: string): Promise<void> {
|
|
const body = { username, password };
|
|
|
|
try {
|
|
// UniFi OS
|
|
const res = await this.client.post("/api/auth/login", body);
|
|
console.log("Login OK (UniFi OS)", res.status);
|
|
this.persistSession(res);
|
|
} catch (e) {
|
|
// Legacy controller
|
|
console.log("UniFi OS login failed, trying legacy...");
|
|
const res = await this.client.post("/api/login", body);
|
|
console.log("Login OK (legacy)", res.status);
|
|
this.persistSession(res);
|
|
}
|
|
}
|
|
|
|
private async fetchWlanConfRaw(site: string): Promise<WlanConfRaw[]> {
|
|
const paths = [
|
|
`/proxy/network/api/s/${site}/rest/wlanconf`,
|
|
`/api/s/${site}/rest/wlanconf`,
|
|
];
|
|
|
|
for (const path of paths) {
|
|
try {
|
|
const res = await this.client.get(path);
|
|
const data = (res.data?.data ?? res.data) as WlanConfRaw[];
|
|
console.log(`Fetched wlan from ${path}`);
|
|
return data;
|
|
} catch (e) {
|
|
console.log(
|
|
`Failed ${path}:`,
|
|
axios.isAxiosError(e) ? e.response?.status : e,
|
|
);
|
|
}
|
|
}
|
|
|
|
throw new Error("Could not fetch WLAN config from any known path");
|
|
}
|
|
|
|
async getWlanConf(site: string): Promise<WlanConf[]> {
|
|
const raw = await this.fetchWlanConfRaw(site);
|
|
|
|
return raw.map(
|
|
(w): WlanConf => ({
|
|
id: w._id,
|
|
ssid: (w.name || w.ssid || "").toString(),
|
|
password: typeof w.x_passphrase === "string" ? w.x_passphrase : null,
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const unifi = new UnifiClient(controllerBaseUrl);
|
|
|
|
try {
|
|
await unifi.login(username, password);
|
|
|
|
const wlans = await unifi.getWlanConf(site);
|
|
|
|
wlans.forEach((wlan) => {
|
|
console.log(`${wlan.ssid}: ${wlan.password ?? "<no password>"}`);
|
|
});
|
|
} catch (err) {
|
|
if (axios.isAxiosError(err)) {
|
|
console.error("HTTP error", err.response?.status, err.response?.data);
|
|
} else {
|
|
console.error("Error", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
main().catch((e) => console.error(e));
|