Stop Matrix refresh loop when login is unavailable
This commit is contained in:
1 parent
12a853fa42
commit
92287c0a9a
2 files changed
+73
-3
No files matched your search
@@ -25,6 +25,13 @@ export interface TokenRefresher {
|
||||
refresh(): Promise<void>;
|
||||
}
|
||||
|
||||
class MatrixAuthenticationUnavailableError extends Error {
|
||||
constructor(cause: unknown) {
|
||||
super('Matrix authentication could not be restored', { cause });
|
||||
this.name = 'MatrixAuthenticationUnavailableError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional Reddit-API hook for resolving t2 IDs to real usernames. Without
|
||||
* it, conversations show up as `t2_abc123` (stable but ugly). With it, the
|
||||
@@ -174,7 +181,11 @@ export class ChatService {
|
||||
} catch (err) {
|
||||
if (err instanceof MatrixAuthError && this.tokenRefresher) {
|
||||
this.logger.warn('[chat] Matrix token rejected — refreshing via browser session');
|
||||
await this.tokenRefresher.refresh();
|
||||
try {
|
||||
await this.tokenRefresher.refresh();
|
||||
} catch (refreshError) {
|
||||
throw new MatrixAuthenticationUnavailableError(refreshError);
|
||||
}
|
||||
return await fn();
|
||||
}
|
||||
throw err;
|
||||
@@ -331,6 +342,7 @@ export class ChatService {
|
||||
try {
|
||||
await this.loadDetails(chat);
|
||||
} catch (err) {
|
||||
if (err instanceof MatrixAuthenticationUnavailableError) throw err;
|
||||
this.logger.error(`[chat] backfill: failed details for ${chat.roomName}`, err);
|
||||
}
|
||||
done++;
|
||||
@@ -1062,6 +1074,10 @@ export class ChatService {
|
||||
}) as { state_key?: string } | undefined;
|
||||
t2 = otherMember?.state_key?.match(/^@(t2_[^:]+):/)?.[1];
|
||||
} catch (err) {
|
||||
if (err instanceof MatrixAuthenticationUnavailableError) {
|
||||
this.logger.warn('[chat] username enrichment paused until Reddit login is restored');
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`[chat] /state lookup failed for ${chat.roomId}:`, err);
|
||||
}
|
||||
if (!t2) {
|
||||
@@ -1074,6 +1090,10 @@ export class ChatService {
|
||||
)?.sender;
|
||||
t2 = otherSender?.match(/^@(t2_[^:]+):/)?.[1];
|
||||
} catch (err) {
|
||||
if (err instanceof MatrixAuthenticationUnavailableError) {
|
||||
this.logger.warn('[chat] username enrichment paused until Reddit login is restored');
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`[chat] /messages lookup failed for ${chat.roomId}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import Database from 'better-sqlite3';
|
||||
@@ -6,7 +6,7 @@ import { runMigrations } from '../src/database/client.js';
|
||||
import { RedditUsersRepository } from '../src/database/repositories.js';
|
||||
import { ChatService } from '../src/services/chat/service.js';
|
||||
import { ChatCache } from '../src/services/chat/cache.js';
|
||||
import { MatrixClient } from '../src/services/chat/matrix/client.js';
|
||||
import { MatrixAuthError, MatrixClient } from '../src/services/chat/matrix/client.js';
|
||||
import { createMockLogger } from './mockLogger.js';
|
||||
|
||||
const creds = {
|
||||
@@ -103,4 +103,54 @@ describe('ChatService username persistence', () => {
|
||||
expect(chats[0].roomName).toBe('alice');
|
||||
db.close();
|
||||
});
|
||||
|
||||
it('aborts username enrichment after one failed token refresh', async () => {
|
||||
const logger = createMockLogger();
|
||||
const cache = tmpCache(logger);
|
||||
cache.set([
|
||||
{
|
||||
username: 'unknown',
|
||||
roomName: 'unknown',
|
||||
lastMessageTime: Date.now(),
|
||||
lastMessageSnippet: '',
|
||||
isFromUs: false,
|
||||
unread: false,
|
||||
roomId: '!first:reddit.com',
|
||||
messages: [],
|
||||
},
|
||||
{
|
||||
username: 'unknown',
|
||||
roomName: 'unknown',
|
||||
lastMessageTime: Date.now(),
|
||||
lastMessageSnippet: '',
|
||||
isFromUs: false,
|
||||
unread: false,
|
||||
roomId: '!second:reddit.com',
|
||||
messages: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const matrix = buildMatrix();
|
||||
matrix.getRoomFullState = vi.fn().mockRejectedValue(new MatrixAuthError('expired'));
|
||||
matrix.getRoomMessages = vi.fn().mockRejectedValue(new MatrixAuthError('expired'));
|
||||
const tokenRefresher = {
|
||||
refresh: vi.fn().mockRejectedValue(new Error('interactive login required')),
|
||||
};
|
||||
const usernameResolver = { resolveT2Ids: vi.fn().mockResolvedValue({}) };
|
||||
const svc = new ChatService(
|
||||
matrix,
|
||||
logger,
|
||||
undefined,
|
||||
undefined,
|
||||
tokenRefresher,
|
||||
usernameResolver,
|
||||
cache,
|
||||
);
|
||||
|
||||
await svc.enrichUsernames();
|
||||
|
||||
expect(tokenRefresher.refresh).toHaveBeenCalledOnce();
|
||||
expect(matrix.getRoomFullState).toHaveBeenCalledOnce();
|
||||
expect(matrix.getRoomMessages).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user