Show Reddit recovery status in dashboard
This commit is contained in:
1 parent
d2b3a1b31d
commit
316dc3dd3e
6 files changed
+208
-5
No files matched your search
+27
-1
@@ -4,10 +4,24 @@ import type { Logger } from '../utils/logger.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
export type RedditSessionState =
|
||||
| 'unknown'
|
||||
| 'authenticated'
|
||||
| 'login-required'
|
||||
| 'unreachable';
|
||||
|
||||
export function classifyRedditSessionError(error: unknown): Exclude<RedditSessionState, 'unknown' | 'authenticated'> {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return /ERR_(?:HTTP_RESPONSE_CODE_FAILURE|CONNECTION|TIMED_OUT|NAME_NOT_RESOLVED|NETWORK_CHANGED)|net::|ENOTFOUND|ECONN(?:REFUSED|RESET)|fetch failed/i.test(message)
|
||||
? 'unreachable'
|
||||
: 'login-required';
|
||||
}
|
||||
|
||||
export class BrowserClient {
|
||||
private context: BrowserContext | null = null;
|
||||
private sessionDir: string | null = null;
|
||||
private sessionValidityInFlight: Promise<boolean> | null = null;
|
||||
private sessionState: RedditSessionState = 'unknown';
|
||||
|
||||
constructor(
|
||||
private config: Config,
|
||||
@@ -51,6 +65,10 @@ export class BrowserClient {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
getSessionState(): RedditSessionState {
|
||||
return this.sessionState;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if (this.context) {
|
||||
this.logger.info('Closing browser');
|
||||
@@ -85,6 +103,7 @@ export class BrowserClient {
|
||||
for (const selector of visibleIndicators) {
|
||||
const el = page.locator(selector).first();
|
||||
if (await el.isVisible().catch(() => false)) {
|
||||
this.sessionState = 'authenticated';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -97,13 +116,17 @@ export class BrowserClient {
|
||||
for (const selector of attributeIndicators) {
|
||||
const el = await page.$(selector);
|
||||
if (el) {
|
||||
this.sessionState = 'authenticated';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to a direct auth check; this avoids false negatives from hidden login buttons.
|
||||
return await this.isLoggedInViaApi(page);
|
||||
const loggedIn = await this.isLoggedInViaApi(page);
|
||||
this.sessionState = loggedIn ? 'authenticated' : 'login-required';
|
||||
return loggedIn;
|
||||
} catch (error) {
|
||||
this.sessionState = classifyRedditSessionError(error);
|
||||
this.logger.error('Failed to check session validity:', error);
|
||||
return false;
|
||||
}
|
||||
@@ -128,6 +151,7 @@ export class BrowserClient {
|
||||
|
||||
async login(): Promise<boolean> {
|
||||
if (!this.config.reddit.username || !this.config.reddit.password) {
|
||||
this.sessionState = 'login-required';
|
||||
this.logger.error('Login failed: Missing credentials in config/env');
|
||||
return false;
|
||||
}
|
||||
@@ -202,6 +226,7 @@ export class BrowserClient {
|
||||
|
||||
const isValid = await this.isSessionValid();
|
||||
if (isValid) {
|
||||
this.sessionState = 'authenticated';
|
||||
this.logger.info('Login successful!');
|
||||
return true;
|
||||
}
|
||||
@@ -236,6 +261,7 @@ export class BrowserClient {
|
||||
return false;
|
||||
|
||||
} catch (error) {
|
||||
this.sessionState = classifyRedditSessionError(error);
|
||||
this.logger.error('Error during automated login:', error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export function createLoginRouter(ctx: WebContext): Router {
|
||||
res.json({
|
||||
vncEnabled: Boolean(process.env.DISPLAY),
|
||||
novncPort,
|
||||
publicUrl: process.env.NOVNC_PUBLIC_URL || null,
|
||||
passwordRequired: Boolean(process.env.VNC_PASSWORD),
|
||||
});
|
||||
});
|
||||
@@ -23,7 +24,7 @@ export function createLoginRouter(ctx: WebContext): Router {
|
||||
}
|
||||
try {
|
||||
const loggedIn = await browser.isSessionValid();
|
||||
res.json({ ready: true, loggedIn });
|
||||
res.json({ ready: true, loggedIn, status: browser.getSessionState() });
|
||||
} catch (error) {
|
||||
ctx.logger.error('Failed to check login status:', error);
|
||||
res.status(500).json({ ready: true, loggedIn: false, error: 'check failed' });
|
||||
|
||||
Reference in new issue
Block a user