Files
Veskslop-t/poc/src/auth-service.ts
T

82 lines
2.2 KiB
TypeScript

/*
* Veskslop daemon — auth state for the web UI.
*
* Owns at most one QR login session. The terminal tool and the web UI share
* the session class. The token file stays the single credential store.
*/
import { existsSync } from "node:fs";
import { isTerminalQrState, QrLoginSession, type QrLoginState, type QrLoginUpdate } from "./qr-session.js";
export interface AuthSessionSnapshot {
state: QrLoginState;
url?: string;
user?: object;
error?: string;
captcha?: object;
}
export interface AuthStatus {
tokenFile: string;
hasToken: boolean;
session: AuthSessionSnapshot | null;
}
export class AuthService {
private session?: QrLoginSession;
private snapshot: AuthSessionSnapshot | null = null;
private freshLogin = false;
constructor(
private readonly tokenFile: string,
private readonly onUpdate?: (update: QrLoginUpdate) => void
) {}
status(): AuthStatus {
return {
tokenFile: this.tokenFile,
hasToken: existsSync(this.tokenFile),
session: this.snapshot ? structuredClone(this.snapshot) : null
};
}
startQr(): AuthStatus {
if (this.session && !isTerminalQrState(this.session.state)) return this.status();
this.snapshot = { state: "connecting" };
this.session = new QrLoginSession({
tokenFile: this.tokenFile,
debug: process.env.DEBUG === "1",
onUpdate: update => {
this.snapshot = { ...update };
if (update.state === "finished") this.freshLogin = true;
this.onUpdate?.(update);
}
});
this.session.start();
return this.status();
}
cancelQr(): AuthStatus {
this.session?.cancel();
return this.status();
}
submitCaptcha(key: string): boolean {
return this.session?.submitCaptcha(key) ?? false;
}
/** True once after a successful login. Used to force the new token on start. */
consumeFreshLogin(): boolean {
const value = this.freshLogin;
this.freshLogin = false;
return value;
}
clearFreshLogin(): void {
this.freshLogin = false;
}
}