feat: support concurrent Reddit accounts
This commit is contained in:
1 parent
d819a0f1e6
commit
16a31335d0
51 files changed
+3241
-250
No files matched your search
+56
-7
@@ -11,19 +11,27 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const items = await ctx.services.media.getMediaItems();
|
||||
const runtimes = ctx.services.accountRuntimes?.list().filter((runtime) => runtime.state === 'running') ?? [];
|
||||
const items = runtimes.length > 0
|
||||
? (await Promise.all(runtimes.map((snapshot) =>
|
||||
ctx.services.accountRuntimes!.get(snapshot.accountId)!.resources.mediaService.getMediaItems(),
|
||||
))).flat()
|
||||
: await ctx.services.media.getMediaItems();
|
||||
|
||||
// Build a roomId → current chat lookup so we can override stale
|
||||
// stored usernames (e.g. items captured before username enrichment ran)
|
||||
// and surface the recipient even for rooms that were originally
|
||||
// ingested as `unknown`.
|
||||
const liveChats = ctx.services.chat
|
||||
? await ctx.services.chat.getCachedConversations()
|
||||
: [];
|
||||
const byRoom = new Map(liveChats.map((c) => [c.roomId ?? '', c] as const));
|
||||
const liveChats = runtimes.length > 0
|
||||
? (await Promise.all(runtimes.map((snapshot) =>
|
||||
ctx.services.accountRuntimes!.get(snapshot.accountId)!.resources.chatService.getCachedConversations()
|
||||
.then((chats) => chats.map((chat) => ({ ...chat, accountId: snapshot.accountId }))),
|
||||
))).flat()
|
||||
: (ctx.services.chat ? await ctx.services.chat.getCachedConversations() : []).map((chat) => ({ ...chat, accountId: '' }));
|
||||
const byRoom = new Map(liveChats.map((c) => [`${c.accountId}:${c.roomId ?? ''}`, c] as const));
|
||||
|
||||
res.json(items.map((item) => {
|
||||
const live = item.chatRoomId ? byRoom.get(item.chatRoomId) : undefined;
|
||||
const live = item.chatRoomId ? byRoom.get(`${item.accountId ?? ''}:${item.chatRoomId}`) : undefined;
|
||||
const username = resolveRedditUsername(
|
||||
live?.username ?? item.chatUsername,
|
||||
ctx.services.chat,
|
||||
@@ -31,11 +39,15 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
const sender = /^(me|you)$/i.test(item.sender)
|
||||
? 'You'
|
||||
: resolveRedditUsername(item.sender, ctx.services.chat) ?? username ?? 'Unknown Reddit user';
|
||||
const account = item.accountId
|
||||
? ctx.services.redditAccounts?.get(item.accountId)
|
||||
: null;
|
||||
return {
|
||||
...item,
|
||||
chatUsername: username,
|
||||
sender,
|
||||
urlPath: `/media/${encodeURIComponent(item.relativePath)}`,
|
||||
urlPath: `/media/${item.relativePath.split(/[\\/]/).map(encodeURIComponent).join('/')}`,
|
||||
owner: account ? { accountId: account.accountId, redditUsername: account.redditUsername } : undefined,
|
||||
};
|
||||
}));
|
||||
} catch (error) {
|
||||
@@ -45,6 +57,21 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
});
|
||||
|
||||
router.get('/media/sync', (_req, res) => {
|
||||
const runtimes = ctx.services.accountRuntimes?.list().filter((runtime) => runtime.state === 'running') ?? [];
|
||||
if (runtimes.length > 0) {
|
||||
const statuses = runtimes.map((snapshot) =>
|
||||
ctx.services.accountRuntimes!.get(snapshot.accountId)!.resources.mediaSyncService.getStatus(),
|
||||
);
|
||||
res.json({
|
||||
isSyncing: statuses.some((status) => status.isSyncing),
|
||||
lastSyncAt: newest(statuses.map((status) => status.lastSyncAt)),
|
||||
lastSyncDurationMs: newest(statuses.map((status) => status.lastSyncDurationMs)),
|
||||
lastSyncChats: statuses.reduce((total, status) => total + (status.lastSyncChats ?? 0), 0) || null,
|
||||
lastError: statuses.find((status) => status.lastError)?.lastError ?? null,
|
||||
nextSyncAt: newest(statuses.map((status) => status.nextSyncAt)),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!ctx.services.mediaSync) {
|
||||
res.status(503).json({ error: 'Media sync service not initialized' });
|
||||
return;
|
||||
@@ -53,6 +80,15 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
});
|
||||
|
||||
router.post('/media/sync', async (req, res) => {
|
||||
const runtimes = ctx.services.accountRuntimes?.list().filter((runtime) => runtime.state === 'running') ?? [];
|
||||
if (runtimes.length > 0) {
|
||||
const full = req.body?.full === true;
|
||||
const results = await Promise.all(runtimes.map((snapshot) =>
|
||||
ctx.services.accountRuntimes!.get(snapshot.accountId)!.resources.mediaSyncService.triggerSync(full),
|
||||
));
|
||||
res.json({ success: results.some(Boolean), reason: results.some(Boolean) ? undefined : 'Sync already in progress' });
|
||||
return;
|
||||
}
|
||||
if (!ctx.services.mediaSync) {
|
||||
res.status(503).json({ error: 'Media sync service not initialized' });
|
||||
return;
|
||||
@@ -73,6 +109,14 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
});
|
||||
|
||||
router.post('/media/sync/full', async (_req, res) => {
|
||||
const runtimes = ctx.services.accountRuntimes?.list().filter((runtime) => runtime.state === 'running') ?? [];
|
||||
if (runtimes.length > 0) {
|
||||
const results = await Promise.all(runtimes.map((snapshot) =>
|
||||
ctx.services.accountRuntimes!.get(snapshot.accountId)!.resources.mediaSyncService.triggerSync(true),
|
||||
));
|
||||
res.json({ success: results.some(Boolean), reason: results.some(Boolean) ? undefined : 'Sync already in progress' });
|
||||
return;
|
||||
}
|
||||
if (!ctx.services.mediaSync) {
|
||||
res.status(503).json({ error: 'Media sync service not initialized' });
|
||||
return;
|
||||
@@ -93,3 +137,8 @@ export function createMediaRouter(ctx: WebContext): Router {
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
function newest(values: Array<number | null>): number | null {
|
||||
const present = values.filter((value): value is number => value !== null);
|
||||
return present.length > 0 ? Math.max(...present) : null;
|
||||
}
|
||||
Reference in new issue
Block a user