82 lines
2.2 KiB
TypeScript
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;
|
|
}
|
|
}
|