mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-06-22 04:11:32 +10:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7b88cf20f | |||
| d060533af8 |
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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.",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
});
|
||||
|
||||
@@ -30,7 +30,9 @@ class AuthManager {
|
||||
(this.authProviders as any)[key] = object;
|
||||
logger.info(`enabled auth: ${key}`);
|
||||
} catch (e) {
|
||||
logger.warn((e as string).toString());
|
||||
logger.warn(
|
||||
`failed to enable auth ${key}: ${(e as string).toString()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,28 +12,22 @@ import * as jose from "jose";
|
||||
// import { inspect } from "util";
|
||||
import sessionHandler from "../../session";
|
||||
import type { SessionSearchTerms } from "../../session/types";
|
||||
import { queryParamBuilder } from "../../utils/query";
|
||||
|
||||
// TODO: monitor https://github.com/goauthentik/authentik/issues/8751 for easier?? OIDC setup by end users
|
||||
|
||||
// Schema for OIDC well-known configuration
|
||||
const OIDCWellKnownV1 = type({
|
||||
issuer: "string.url.parse",
|
||||
issuer: "string",
|
||||
authorization_endpoint: "string.url.parse",
|
||||
token_endpoint: "string.url.parse",
|
||||
userinfo_endpoint: "string.url.parse?",
|
||||
userinfo_endpoint: "string.url.parse",
|
||||
jwks_uri: "string.url.parse",
|
||||
scopes_supported: "string[]?",
|
||||
scopes_supported: "string[]",
|
||||
});
|
||||
|
||||
// Represents required OIDC configuration
|
||||
interface OIDCConfiguration {
|
||||
issuer: URL;
|
||||
authorization_endpoint: URL;
|
||||
token_endpoint: URL;
|
||||
userinfo_endpoint: URL;
|
||||
jwks_uri: URL;
|
||||
scopes_supported: string[];
|
||||
}
|
||||
type OIDCConfiguration = typeof OIDCWellKnownV1.infer;
|
||||
|
||||
interface OIDCAuthSessionOptions {
|
||||
redirect: string | undefined;
|
||||
@@ -241,7 +235,7 @@ export class OIDCManager {
|
||||
token_endpoint: new URL(tokenEndpoint),
|
||||
userinfo_endpoint: new URL(userinfoEndpoint),
|
||||
scopes_supported: scopes.split(","),
|
||||
issuer: new URL(issuer),
|
||||
issuer: issuer,
|
||||
jwks_uri: new URL(jwksEndpoint),
|
||||
};
|
||||
}
|
||||
@@ -294,7 +288,15 @@ export class OIDCManager {
|
||||
this.oidcConfiguration.authorization_endpoint,
|
||||
).toString();
|
||||
|
||||
const finalUrl = `${normalisedUrl}?client_id=${this.clientId}&redirect_uri=${encodeURIComponent(this.redirectUrl.toString())}&state=${stateKey}&response_type=code&scope=${encodeURIComponent(this.oidcConfiguration.scopes_supported.join(" "))}`;
|
||||
const queryParams = queryParamBuilder({
|
||||
client_id: this.clientId,
|
||||
redirect_uri: this.redirectUrl.toString(),
|
||||
state: stateKey,
|
||||
response_type: "code",
|
||||
scope: this.oidcConfiguration.scopes_supported.join(" "),
|
||||
});
|
||||
|
||||
const finalUrl = `${normalisedUrl}?${queryParams}`;
|
||||
|
||||
const session: OIDCAuthSession = {
|
||||
redirectUrl: finalUrl,
|
||||
@@ -549,7 +551,8 @@ export class OIDCManager {
|
||||
}
|
||||
}
|
||||
|
||||
function isHttps(url: URL): boolean {
|
||||
if (url.protocol === "https:") return true;
|
||||
function isHttps(url: URL | string): boolean {
|
||||
const parsedUrl = typeof url === "string" ? new URL(url) : url;
|
||||
if (parsedUrl.protocol === "https:") return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,8 +17,8 @@ export const taskGroups = {
|
||||
"import:version": {
|
||||
concurrency: true,
|
||||
},
|
||||
debug: {
|
||||
concurrency: true,
|
||||
"import:check-integrity": {
|
||||
concurrency: false,
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export function queryParamBuilder(params: { [key: string]: string }) {
|
||||
const list = Object.entries(params).map(
|
||||
([key, value]) => `${key}=${encodeURIComponent(value)}`,
|
||||
);
|
||||
const str = list.join("&");
|
||||
return str;
|
||||
}
|
||||
Reference in New Issue
Block a user