implement recovery from changed token
use exponential backoff for token retrieval
This commit is contained in:
4
bun.lock
4
bun.lock
@@ -1,9 +1,11 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nanokvm-mqtt",
|
"name": "nanokvm-mqtt",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"exponential-backoff": "^3.1.3",
|
||||||
"mqtt": "^5.14.1",
|
"mqtt": "^5.14.1",
|
||||||
"tsdown": "^0.16.4",
|
"tsdown": "^0.16.4",
|
||||||
"tslog": "^4.10.2",
|
"tslog": "^4.10.2",
|
||||||
@@ -155,6 +157,8 @@
|
|||||||
|
|
||||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||||
|
|
||||||
|
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
|
||||||
|
|
||||||
"fast-unique-numbers": ["fast-unique-numbers@9.0.24", "", { "dependencies": { "@babel/runtime": "^7.28.4", "tslib": "^2.8.1" } }, "sha512-Dv0BYn4waOWse94j16rsZ5w/0zoaCa74O3q6IZjMqaXbtT92Q+Sb6pPk+phGzD8Xh+nueQmSRI3tSCaHKidzKw=="],
|
"fast-unique-numbers": ["fast-unique-numbers@9.0.24", "", { "dependencies": { "@babel/runtime": "^7.28.4", "tslib": "^2.8.1" } }, "sha512-Dv0BYn4waOWse94j16rsZ5w/0zoaCa74O3q6IZjMqaXbtT92Q+Sb6pPk+phGzD8Xh+nueQmSRI3tSCaHKidzKw=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"exponential-backoff": "^3.1.3",
|
||||||
"mqtt": "^5.14.1",
|
"mqtt": "^5.14.1",
|
||||||
"tsdown": "^0.16.4",
|
"tsdown": "^0.16.4",
|
||||||
"tslog": "^4.10.2",
|
"tslog": "^4.10.2",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { BodyInit } from "bun";
|
import type { BodyInit } from "bun";
|
||||||
|
import { backOff } from "exponential-backoff";
|
||||||
import type z from "zod";
|
import type z from "zod";
|
||||||
import { encryptPassword } from "@/encryption";
|
import { encryptPassword } from "@/encryption";
|
||||||
import { logger } from "@/logger";
|
import { logger } from "@/logger";
|
||||||
@@ -30,7 +31,21 @@ export class NanoKVMClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loginAndSetToken();
|
await this.initAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
async initAuth() {
|
||||||
|
logger.info("Initializing auth...");
|
||||||
|
|
||||||
|
await backOff(async () => await this.loginAndSetToken(), {
|
||||||
|
numOfAttempts: 50,
|
||||||
|
retry: (_, attempt) => {
|
||||||
|
logger.info(`Retrying for the ${attempt} time`);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
startingDelay: 100,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(path: string, method?: "POST" | "GET", body?: BodyInit) {
|
async fetch(path: string, method?: "POST" | "GET", body?: BodyInit) {
|
||||||
@@ -75,15 +90,19 @@ export class NanoKVMClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case -2: {
|
case -2: {
|
||||||
logger.fatal(`Invalid password! Failed to get token.`);
|
logger.fatal(
|
||||||
|
`Invalid password! Failed to get token for ${this.options.host}`,
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
throw new Error("Failed to get token!");
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
logger.fatal(
|
logger.fatal(
|
||||||
"Unknown error while getting token.",
|
"Unknown error while getting token.",
|
||||||
(result as z.output<typeof AuthLoginSchema>).msg,
|
(result as z.output<typeof AuthLoginSchema>).msg,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
throw new Error("Failed to get token!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type z from "zod";
|
import z from "zod";
|
||||||
|
import { logger } from "@/logger";
|
||||||
import { NanoKVMClient } from "./nano-kvm-client";
|
import { NanoKVMClient } from "./nano-kvm-client";
|
||||||
import {
|
import {
|
||||||
type GpioSchema,
|
type GpioSchema,
|
||||||
@@ -26,7 +27,20 @@ export class NanoKVMService {
|
|||||||
async getInfo(): Promise<z.output<typeof InfoSchema>> {
|
async getInfo(): Promise<z.output<typeof InfoSchema>> {
|
||||||
const data = await (await this.client.fetch("/api/vm/info", "GET")).json();
|
const data = await (await this.client.fetch("/api/vm/info", "GET")).json();
|
||||||
|
|
||||||
return InfoSuccess.parse(data).data;
|
const result = InfoSuccess.safeParse(data);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
logger.error(data);
|
||||||
|
logger.fatal(`Failed getting info: `, z.prettifyError(result.error));
|
||||||
|
|
||||||
|
if (data === "unauthorized") {
|
||||||
|
await this.client.initAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGpio(): Promise<z.output<typeof GpioSchema>> {
|
async getGpio(): Promise<z.output<typeof GpioSchema>> {
|
||||||
@@ -34,7 +48,20 @@ export class NanoKVMService {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
return GpioSuccess.parse(data).data;
|
const result = GpioSuccess.safeParse(data);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
logger.error(data);
|
||||||
|
logger.fatal(`Failed getting gpio: `, z.prettifyError(result.error));
|
||||||
|
|
||||||
|
if (data === "unauthorized") {
|
||||||
|
await this.client.initAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async triggerPower(input: z.input<typeof TriggerPowerInput>) {
|
async triggerPower(input: z.input<typeof TriggerPowerInput>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user