feat(rubgram): change archive strategy to monthly incremental

This commit is contained in:
2026-04-13 21:03:15 +07:00 Unverified
parent 2d7ec23bdc
commit 1bb2b93f8d
5 changed files with 67 additions and 13 deletions
+13 -3
View File
@@ -43,9 +43,11 @@ import { setFree, setLimit, toggleCheck, toggleLock } from "../api";
export function SidebarLink({
submission,
className,
prefetch = true,
}: {
submission: { id: string; name: string; checked: boolean; queue: number };
className?: string;
prefetch?: boolean;
}) {
const { stop } = useProgress();
const { id } = useParams();
@@ -62,7 +64,7 @@ export function SidebarLink({
className,
id === submission.id && "bg-accent text-accent-foreground",
)}
prefetch
prefetch={prefetch}
onClick={(ev) => {
if ((ev.target as HTMLButtonElement).type === "button") {
ev.preventDefault();
@@ -250,10 +252,17 @@ export function SubmissionList({
checked: boolean;
queue: number;
paid: boolean;
archived: boolean;
}[];
}) {
const [query, setQuery] = useState("");
const [debug] = shared.state("debug");
const bottomRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
// Scrolls to the dummy div whenever 'items' changes
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, []);
return (
<>
@@ -272,11 +281,12 @@ export function SubmissionList({
<SidebarMenuButton
key={s.id}
asChild
className={cn(debug && !s.paid && "opacity-50")}
className={cn(((debug && !s.paid) || s.archived) && "opacity-50")}
>
<SidebarLink submission={s} />
<SidebarLink submission={s} prefetch={!s.archived} />
</SidebarMenuButton>
))}
<div ref={bottomRef} />
</>
);
}
+3 -1
View File
@@ -29,6 +29,7 @@ import { db } from "@/lib/db";
import { endgameSubmissions } from "@/lib/db/schema";
import { getEndgameConfig, random, wipe } from "../api";
import { LimitManager, SlipButton, SubmissionList, Watcher } from "./client";
import { desc } from "drizzle-orm";
export default async function AdminLayout({
children,
@@ -43,9 +44,10 @@ export default async function AdminLayout({
checked: endgameSubmissions.checked,
queue: endgameSubmissions.queue,
paid: endgameSubmissions.paid,
archived: endgameSubmissions.archived,
})
.from(endgameSubmissions)
.orderBy(endgameSubmissions.id);
.orderBy(desc(endgameSubmissions.archived), endgameSubmissions.id);
const config = await getEndgameConfig();
return (
<SidebarProvider>
+25 -6
View File
@@ -30,17 +30,31 @@ const {
BASE_URL,
} = process.env as Record<string, string>;
export async function wipe() {
export async function archive({ onlyChecked = true, copy = true }) {
if (!(await adminCheck())) throw "Unauthorized";
await db.transaction(async (tx) => {
const deleted = await tx.delete(endgameSubmissions).returning();
await tx.execute(
sql`ALTER SEQUENCE endgame.submissions_queue_seq RESTART WITH 1`,
const deleted = await (copy
? tx
.update(endgameSubmissions)
.set({
archived: true,
})
.returning()
: tx.delete(endgameSubmissions).returning()
).where(
and(
onlyChecked ? eq(endgameSubmissions.checked, true) : undefined,
not(endgameSubmissions.archived),
),
);
const [{ maxRound }] = await db
if (!onlyChecked && !copy)
await tx.execute(
sql`ALTER SEQUENCE endgame.submissions_queue_seq RESTART WITH 1`,
);
const [{ maxRound }] = await tx
.select({ maxRound: sql<number>`MAX(${endgameArchive.round})` })
.from(endgameArchive);
await db.insert(endgameArchive).values(
await tx.insert(endgameArchive).values(
deleted
.filter((e) => e.paid)
.map((s) => ({
@@ -49,6 +63,11 @@ export async function wipe() {
})),
);
});
}
export async function wipe() {
if (!(await adminCheck())) throw "Unauthorized";
await archive({ onlyChecked: false });
revalidatePath("/rubgram/admin");
revalidatePath("/rubgram");
+25 -3
View File
@@ -1,9 +1,9 @@
import { env } from "node:process";
import { redis } from "bun";
import { cron, redis } from "bun";
import { formatDistanceToNow } from "date-fns";
import { and, asc, eq, gt, isNull, lt, not, sql } from "drizzle-orm";
import { removeExpiredSubmissions } from "@/app/(ui)/rubgram/api";
import { auth } from "@/lib/auth";
import { archive, removeExpiredSubmissions } from "@/app/(ui)/rubgram/api";
import { auth, issueInternalToken } from "@/lib/auth";
import { db } from "@/lib/db";
import {
artifactSettings,
@@ -272,6 +272,28 @@ async function cacheCards() {
} else schedule(120, cacheCards);
}
cron("0 0 1 * *", async function rubgramArchive() {
const { log } = logger("rubgramArchive");
log("Started...");
await archive({});
log("Done");
});
cron("0 0 /14 * *", async function syncAmber() {
const { log, error } = logger("syncAmber");
log("Syncing...");
const token = await issueInternalToken();
const res = await fetch(`http://app:3000/api/amber/sync`, {
headers: {
"X-Internal-Auth": token,
},
}).catch(() => ({
ok: false,
}));
if (res.ok) log("Success!");
else error("Sync failed.");
});
const redisSubscribers: Record<
string,
(payload: { data: unknown; event?: string }) => void
+1
View File
@@ -151,6 +151,7 @@ export const endgameSubmissions = endgame.table("submissions", {
isNotNull(endgameSubmissions.slip),
)!,
),
archived: boolean().notNull().default(false),
});
export const endgameArchive = endgame.table("sarchive", {