fix: client route type hacking

This commit is contained in:
DecDuck
2025-08-10 11:36:10 +10:00
parent 94e795787e
commit 824b4e708b
3 changed files with 40 additions and 30 deletions

View File

@ -15,6 +15,7 @@ const GetChunk = type({
* Part of v2 download API. Intended to be client-only.
*
* Returns raw stream of all files requested, in order.
* @response `application/octet-stream` stream of all files concatenated
*/
export default defineEventHandler<{ body: typeof GetChunk.infer }>(
async (h3) => {

View File

@ -1,5 +1,5 @@
import { type } from "arktype";
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
import { throwingArktype } from "~/server/arktype";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import contextManager from "~/server/internal/downloads/coordinator";
@ -11,17 +11,13 @@ const CreateContext = type({
/**
* Part of v2 download API. Create a download context for use with `/api/v2/client/chunk`.
*/
export default defineClientEventHandler<{ body: typeof CreateContext.infer }>(
async (h3) => {
const body = await readDropValidatedBody(h3, CreateContext);
export default defineClientEventHandler(async (h3, { body }) => {
const context = await contextManager.createContext(body.game, body.version);
if (!context)
throw createError({
statusCode: 400,
statusMessage: "Invalid game or version",
});
const context = await contextManager.createContext(body.game, body.version);
if (!context)
throw createError({
statusCode: 400,
statusMessage: "Invalid game or version",
});
return { context };
},
);
return { context };
}, CreateContext);

View File

@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ClientModel, UserModel } from "~/prisma/client/models";
import type { EventHandlerRequest, EventHandlerResponse, H3Event } from "h3";
import type { EventHandlerResolver, EventHandlerResponse, H3Event } from "h3";
import droplet from "@drop-oss/droplet";
import prisma from "../db/database";
import { useCertificateAuthority } from "~/server/plugins/ca";
import type { Type } from "arktype";
import { readDropValidatedBody } from "~/server/arktype";
export type EventHandlerFunction<T> = (
h3: H3Event<EventHandlerRequest>,
utils: ClientUtils,
) => Promise<T> | T;
type ClientUtils = {
type ClientUtils<R> = {
body: R;
clientId: string;
fetchClient: () => Promise<ClientModel>;
fetchUser: () => Promise<UserModel>;
@ -17,17 +16,26 @@ type ClientUtils = {
const NONCE_LENIENCE = 30_000;
interface ClientHandler<
R extends EventHandlerRequest = EventHandlerRequest,
K extends EventHandlerResponse = EventHandlerResponse,
type ClientEventHandlerRequest<T> = {
body: T;
query: { [key: string]: string | string[] };
};
interface ClientEventHandler<
R = any,
Request extends ClientEventHandlerRequest<R> = ClientEventHandlerRequest<R>,
Response extends EventHandlerResponse = EventHandlerResponse,
> {
(event: H3Event<R>, utils: ClientUtils): K;
__is_handler__?: true;
__resolve__?: EventHandlerResolver;
(event: H3Event<Request>, utils: ClientUtils<R>): Response;
}
export function defineClientEventHandler<
R extends EventHandlerRequest = EventHandlerRequest,
K = EventHandlerResponse,
>(handler: ClientHandler<R, K>) {
R = any,
T extends ClientEventHandlerRequest<R> = ClientEventHandlerRequest<R>,
K extends EventHandlerResponse = EventHandlerResponse,
>(handler: ClientEventHandler<R, T, K>, validator?: Type<R>) {
return defineEventHandler(async (h3) => {
const header = getHeader(h3, "Authorization");
if (!header) throw createError({ statusCode: 403 });
@ -128,10 +136,11 @@ export function defineClientEventHandler<
return client.user;
}
const utils: ClientUtils = {
const utils: ClientUtils<unknown> = {
clientId,
fetchClient,
fetchUser,
body: undefined,
};
await prisma.client.update({
@ -139,6 +148,10 @@ export function defineClientEventHandler<
data: { lastConnected: new Date() },
});
return await handler(h3, utils);
const body = validator
? await readDropValidatedBody(h3, validator)
: undefined;
return await handler(h3, { ...utils, body: body as R });
});
}