feat: add cloud save backend

This commit is contained in:
DecDuck
2025-04-01 21:08:57 +11:00
parent e7109e58bb
commit 36e6c92938
26 changed files with 642 additions and 35 deletions

View File

@ -4,7 +4,6 @@ import { useCertificateAuthority } from "~/server/plugins/ca";
import prisma from "../db/database";
import { ClientCapabilities } from "@prisma/client";
// These values are technically mapped to the database,
// but Typescript/Prisma doesn't let me link them
// They are also what are required by clients in the API
@ -12,6 +11,7 @@ import { ClientCapabilities } from "@prisma/client";
export enum InternalClientCapability {
PeerAPI = "peerAPI",
UserStatus = "userStatus",
CloudSaves = "cloudSaves",
}
export const validCapabilities = Object.values(InternalClientCapability);
@ -19,6 +19,7 @@ export const validCapabilities = Object.values(InternalClientCapability);
export type CapabilityConfiguration = {
[InternalClientCapability.PeerAPI]: { endpoints: string[] };
[InternalClientCapability.UserStatus]: {};
[InternalClientCapability.CloudSaves]: {};
};
class CapabilityManager {
@ -75,6 +76,7 @@ class CapabilityManager {
return valid;
},
[InternalClientCapability.UserStatus]: async () => true, // No requirements for user status
[InternalClientCapability.CloudSaves]: async () => true, // No requirements for cloud saves
};
async validateCapabilityConfiguration(
@ -82,6 +84,7 @@ class CapabilityManager {
configuration: object
) {
const validationFunction = this.validationFunctions[capability];
if (!validationFunction) return false;
return validationFunction(configuration);
}
@ -90,8 +93,11 @@ class CapabilityManager {
rawCapability: object,
clientId: string
) {
switch (capability) {
case InternalClientCapability.PeerAPI:
const upsertFunctions: EnumDictionary<
InternalClientCapability,
() => Promise<void> | void
> = {
[InternalClientCapability.PeerAPI]: async function () {
const configuration =
rawCapability as CapabilityConfiguration[InternalClientCapability.PeerAPI];
@ -127,9 +133,32 @@ class CapabilityManager {
},
},
});
return;
}
throw new Error("Cannot upsert client capability for: " + capability);
},
[InternalClientCapability.UserStatus]: function (): Promise<void> | void {
throw new Error("Function not implemented.");
},
[InternalClientCapability.CloudSaves]: async function () {
const currentClient = await prisma.client.findUnique({
where: { id: clientId },
select: {
capabilities: true,
},
});
if (!currentClient) throw new Error("Invalid client ID");
if (currentClient.capabilities.includes(ClientCapabilities.CloudSaves))
return;
await prisma.client.update({
where: { id: clientId },
data: {
capabilities: {
push: ClientCapabilities.CloudSaves,
},
},
});
},
};
await upsertFunctions[capability]();
}
}

View File

@ -25,6 +25,16 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
let clientId: string;
switch (method) {
case "Debug":
if (!process.dev) throw createError({ statusCode: 403 });
const client = await prisma.client.findFirst({ select: { id: true } });
if (!client)
throw createError({
statusCode: 400,
statusMessage: "No clients created.",
});
clientId = client.id;
break;
case "Nonce":
clientId = parts[0];
const nonce = parts[1];
@ -49,7 +59,9 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
}
const certificateAuthority = useCertificateAuthority();
const certBundle = await certificateAuthority.fetchClientCertificate(clientId);
const certBundle = await certificateAuthority.fetchClientCertificate(
clientId
);
// This does the blacklist check already
if (!certBundle)
throw createError({