1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
import { authedProcedure, router } from "../trpc";
import { z } from "zod";
import { TRPCError } from "@trpc/server";
import { count } from "drizzle-orm";
import { bookmarks, users } from "@hoarder/db/schema";
import {
LinkCrawlerQueue,
OpenAIQueue,
SearchIndexingQueue,
} from "@hoarder/shared/queues";
const adminProcedure = authedProcedure.use(function isAdmin(opts) {
const user = opts.ctx.user;
if (user.role != "admin") {
throw new TRPCError({ code: "FORBIDDEN" });
}
return opts.next(opts);
});
export const adminAppRouter = router({
stats: adminProcedure
.output(
z.object({
numUsers: z.number(),
numBookmarks: z.number(),
pendingCrawls: z.number(),
pendingIndexing: z.number(),
pendingOpenai: z.number(),
}),
)
.query(async ({ ctx }) => {
const [
[{ value: numUsers }],
[{ value: numBookmarks }],
pendingCrawls,
pendingIndexing,
pendingOpenai,
] = await Promise.all([
ctx.db.select({ value: count() }).from(users),
ctx.db.select({ value: count() }).from(bookmarks),
LinkCrawlerQueue.getWaitingCount(),
SearchIndexingQueue.getWaitingCount(),
OpenAIQueue.getWaitingCount(),
]);
return {
numUsers,
numBookmarks,
pendingCrawls,
pendingIndexing,
pendingOpenai,
};
}),
recrawlAllLinks: adminProcedure.mutation(async ({ ctx }) => {
const bookmarkIds = await ctx.db.query.bookmarkLinks.findMany({
columns: {
id: true,
},
});
await Promise.all(
bookmarkIds.map((b) =>
LinkCrawlerQueue.add("crawl", {
bookmarkId: b.id,
}),
),
);
}),
reindexAllBookmarks: adminProcedure.mutation(async ({ ctx }) => {
const bookmarkIds = await ctx.db.query.bookmarks.findMany({
columns: {
id: true,
},
});
await Promise.all(
bookmarkIds.map((b) =>
SearchIndexingQueue.add("search_indexing", {
bookmarkId: b.id,
type: "index",
}),
),
);
}),
});
|