Check integrity task (#364)

This commit is contained in:
DecDuck
2026-03-01 10:49:34 +00:00
committed by GitHub
parent d060533af8
commit b7b88cf20f
8 changed files with 136 additions and 32 deletions
+1
View File
@@ -755,6 +755,7 @@
"cleanupSessionsDescription": "Cleans up expired sessions to save space and ensure security.",
"cleanupSessionsName": "Clean up sessions."
},
"utilityTitle": "Utility tasks",
"viewTask": "View {arrow}",
"weeklyScheduledTitle": "Weekly scheduled tasks"
}
+42 -4
View File
@@ -166,6 +166,44 @@
</div>
</li>
</ul>
<h2 class="text-sm font-medium text-zinc-400 mt-8">
{{ $t("tasks.admin.utilityTitle") }}
</h2>
<ul role="list" class="mt-4 grid grid-cols-1 lg:grid-cols-2 gap-6">
<li
v-for="task in other"
:key="task"
class="col-span-1 divide-y divide-gray-200 rounded-lg bg-zinc-800 border border-zinc-700 shadow-sm"
>
<div class="flex w-full items-center justify-between space-x-6 p-6">
<div class="flex-1">
<div class="flex items-center space-x-2">
<h3 class="text-sm font-medium text-zinc-100">
{{ scheduledTasks[task].name }}
</h3>
</div>
<p class="mt-1 text-sm text-zinc-400">
{{ scheduledTasks[task].description }}
</p>
<button
class="mt-3 rounded-md text-xs font-medium text-zinc-100 hover:text-zinc-300 focus:outline-none focus:ring-2 focus:ring-zinc-100 focus:ring-offset-2"
@click="() => startTask(task)"
>
<i18n-t
keypath="tasks.admin.execute"
tag="span"
scope="global"
class="inline-flex items-center gap-x-1"
>
<template #arrow>
<PlayIcon class="size-4" aria-hidden="true" />
</template>
</i18n-t>
</button>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
@@ -185,7 +223,7 @@ definePageMeta({
const { t } = useI18n();
const { runningTasks, historicalTasks, dailyTasks, weeklyTasks } =
const { runningTasks, historicalTasks, dailyTasks, weeklyTasks, other } =
await $dropFetch("/api/v1/admin/task");
const liveRunningTasks = ref(
@@ -219,9 +257,9 @@ const scheduledTasks: {
name: "",
description: "",
},
debug: {
name: "",
description: "",
"import:check-integrity": {
name: "Check Integrity",
description: "Re-imports all versions and updates their manifests.",
},
};
+3 -1
View File
@@ -1,6 +1,7 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import taskHandler from "~/server/internal/tasks";
import type { TaskGroup } from "~/server/internal/tasks/group";
export default defineEventHandler(async (h3) => {
const allowed = await aclManager.allowSystemACL(h3, ["task:read"]);
@@ -38,6 +39,7 @@ export default defineEventHandler(async (h3) => {
});
const dailyTasks = await taskHandler.dailyTasks();
const weeklyTasks = await taskHandler.weeklyTasks();
const other: TaskGroup[] = ["import:check-integrity"];
return { runningTasks, historicalTasks, dailyTasks, weeklyTasks };
return { runningTasks, historicalTasks, dailyTasks, weeklyTasks, other };
});
@@ -217,7 +217,7 @@ class DropletInterfaceManager {
run: async (message) => {
const callbacks = this.callbacks.get(message.messageId);
if (!callbacks) {
logger.warn(
logger.debug(
`got a droplet message with old message id: ${message.type}, ${message.messageId}`,
);
return undefined;
+2 -2
View File
@@ -17,8 +17,8 @@ export const taskGroups = {
"import:version": {
concurrency: true,
},
debug: {
concurrency: true,
"import:check-integrity": {
concurrency: false,
},
} as const;
+7 -6
View File
@@ -1,11 +1,6 @@
import type { MinimumRequestObject } from "~/server/h3";
import type { GlobalACL } from "../acls";
import aclManager from "../acls";
import cleanupInvites from "./registry/invitations";
import cleanupSessions from "./registry/sessions";
import checkUpdate from "./registry/update";
import cleanupObjects from "./registry/objects";
import { taskGroups, type TaskGroup } from "./group";
import prisma from "../db/database";
import { ArkErrors, type } from "arktype";
@@ -13,6 +8,12 @@ import pino from "pino";
import { logger } from "~/server/internal/logging";
import { Writable } from "node:stream";
import cleanupInvites from "./registry/invitations";
import cleanupSessions from "./registry/sessions";
import checkUpdate from "./registry/update";
import cleanupObjects from "./registry/objects";
import checkIntegrity from "./registry/check-integrity";
type TaskActionLink = `${string}:${string}`;
// a task that has been run
@@ -65,7 +66,7 @@ class TaskHandler {
this.saveScheduledTask(cleanupSessions);
this.saveScheduledTask(checkUpdate);
this.saveScheduledTask(cleanupObjects);
//this.saveScheduledTask(debug);
this.saveScheduledTask(checkIntegrity);
}
/**
@@ -0,0 +1,80 @@
import prisma from "~/server/internal/db/database";
import { defineDropTask, wrapTaskContext } from "..";
import { libraryManager } from "../../library";
export default defineDropTask({
buildId: () => `import:check-integrity:${new Date().toISOString()}`,
name: "Check version integrity",
acls: ["system:import:version:read"],
taskGroup: "import:check-integrity",
async run({ progress, logger, addAction }) {
const versions = await prisma.gameVersion.findMany({
where: {
versionPath: {
not: null,
},
},
select: {
versionId: true,
versionPath: true,
displayName: true,
game: {
select: {
libraryId: true,
libraryPath: true,
mName: true,
},
},
},
});
logger.info(`Checking version integrity for ${versions.length} versions`);
let i = 0;
const progressStep = 100 / versions.length;
for (const version of versions) {
const displayName = `${version.game.mName} ${version.displayName ?? version.versionPath}`;
logger.info(`Starting integrity check for ${displayName}`);
const library = await libraryManager.getLibrary(version.game.libraryId);
if (!library) {
logger.warn(`No library for ${displayName}`);
continue;
}
const min = i * progressStep;
const max = (i + 1) * progressStep;
const taskContext = wrapTaskContext(
{ progress, logger, addAction },
{ min, max, prefix: `re-check ${displayName}` },
);
const manifest = await library.generateDropletManifest(
version.game.libraryPath,
version.versionPath!,
taskContext.progress,
(value) => {
taskContext.logger.info(value);
},
);
// SAFETY: this is requested from the database
// eslint-disable-next-line drop/no-prisma-delete
await prisma.gameVersion.update({
where: {
versionId: version.versionId,
},
data: {
versionId: crypto.randomUUID(),
dropletManifest: manifest,
},
});
logger.info(`Finished integrity check for ${displayName}`);
i++;
}
logger.info("Done");
progress(100);
},
});
-18
View File
@@ -1,18 +0,0 @@
import { defineDropTask } from "..";
export default defineDropTask({
buildId: () => `debug:${new Date().toISOString()}`,
name: "Debug Task",
acls: ["system:maintenance:read"],
taskGroup: "debug",
async run({ progress, logger }) {
const amount = 1000;
for (let i = 0; i < amount; i++) {
progress((i / amount) * 100);
logger.info(`dajksdkajd ${i}`);
logger.warn("warning");
logger.error("error\nmultiline and stuff\nwoah more lines");
await new Promise((r) => setTimeout(r, 1500));
}
},
});