mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-21 04:01:10 +10:00
partial: user routes
This commit is contained in:
@ -2,14 +2,10 @@
|
|||||||
<div class="min-h-screen flex flex-col">
|
<div class="min-h-screen flex flex-col">
|
||||||
<div class="grow grid grid-cols-1 lg:grid-cols-2">
|
<div class="grow grid grid-cols-1 lg:grid-cols-2">
|
||||||
<div class="border-b lg:border-b-0 lg:border-r border-zinc-700">
|
<div class="border-b lg:border-b-0 lg:border-r border-zinc-700">
|
||||||
<header
|
<header class="mx-auto w-full max-w-7xl px-6 pt-6 sm:pt-10 lg:col-span-2 lg:col-start-1 lg:row-start-1 lg:px-8">
|
||||||
class="mx-auto w-full max-w-7xl px-6 pt-6 sm:pt-10 lg:col-span-2 lg:col-start-1 lg:row-start-1 lg:px-8"
|
|
||||||
>
|
|
||||||
<DropWordmark />
|
<DropWordmark />
|
||||||
</header>
|
</header>
|
||||||
<main
|
<main class="mx-auto w-full max-w-7xl px-6 py-24 sm:py-32 lg:col-span-2 lg:col-start-1 lg:row-start-2 lg:px-8">
|
||||||
class="mx-auto w-full max-w-7xl px-6 py-24 sm:py-32 lg:col-span-2 lg:col-start-1 lg:row-start-2 lg:px-8"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-4xl font-display font-bold text-zinc-100">
|
<h1 class="text-4xl font-display font-bold text-zinc-100">
|
||||||
{{ $t("setup.welcome") }}
|
{{ $t("setup.welcome") }}
|
||||||
@ -20,32 +16,19 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul role="list" class="mt-10 divide-y divide-zinc-700/5">
|
<ul role="list" class="mt-10 divide-y divide-zinc-700/5">
|
||||||
<li
|
<li v-for="(action, actionIdx) in actions" :key="action.name" class="relative flex gap-x-6 py-6">
|
||||||
v-for="(action, actionIdx) in actions"
|
|
||||||
:key="action.name"
|
|
||||||
class="relative flex gap-x-6 py-6"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="flex size-10 flex-none items-center justify-center rounded-lg shadow-xs outline-1 outline-zinc-100/10"
|
class="flex size-10 flex-none items-center justify-center rounded-lg shadow-xs outline-1 outline-zinc-100/10">
|
||||||
>
|
<component :is="action.icon" v-if="!actionsComplete[actionIdx]" class="size-6 text-blue-500"
|
||||||
<component
|
aria-hidden="true" />
|
||||||
:is="action.icon"
|
|
||||||
v-if="!actionsComplete[actionIdx]"
|
|
||||||
class="size-6 text-blue-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<CheckIcon v-else class="size-6 text-blue-500" />
|
<CheckIcon v-else class="size-6 text-blue-500" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-auto">
|
<div class="flex-auto">
|
||||||
<h3 class="text-sm/6 font-semibold text-zinc-100">
|
<h3 class="text-sm/6 font-semibold text-zinc-100">
|
||||||
<button
|
<button :class="actionsComplete[actionIdx]
|
||||||
:class="
|
|
||||||
actionsComplete[actionIdx]
|
|
||||||
? 'line-through text-zinc-300'
|
? 'line-through text-zinc-300'
|
||||||
: ''
|
: ''
|
||||||
"
|
" @click="() => (currentAction = actionIdx)">
|
||||||
@click="() => (currentAction = actionIdx)"
|
|
||||||
>
|
|
||||||
<span class="absolute inset-0" aria-hidden="true" />
|
<span class="absolute inset-0" aria-hidden="true" />
|
||||||
{{ action.name }}
|
{{ action.name }}
|
||||||
</button>
|
</button>
|
||||||
@ -55,18 +38,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-none self-center">
|
<div class="flex-none self-center">
|
||||||
<ChevronRightIcon
|
<ChevronRightIcon class="size-5 text-gray-400" aria-hidden="true" />
|
||||||
class="size-5 text-gray-400"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<LoadingButton
|
<LoadingButton :disabled="!finished" :loading="finishLoading" @click="() => finish()">
|
||||||
:disabled="!finished"
|
|
||||||
:loading="finishLoading"
|
|
||||||
@click="() => finish()"
|
|
||||||
>
|
|
||||||
<i18n-t keypath="setup.finish" tag="span" scope="global">
|
<i18n-t keypath="setup.finish" tag="span" scope="global">
|
||||||
<template #arrow>
|
<template #arrow>
|
||||||
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
|
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
|
||||||
@ -75,25 +51,13 @@
|
|||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<component
|
<component :is="actions[currentAction].page" v-if="actions[currentAction] && !useModal"
|
||||||
:is="actions[currentAction].page"
|
v-model="actionsComplete[currentAction]" :token="bearerToken" />
|
||||||
v-if="actions[currentAction] && !useModal"
|
<div v-else-if="!useModal" class="bg-zinc-950/30 flex items-center justify-center">
|
||||||
v-model="actionsComplete[currentAction]"
|
|
||||||
:token="bearerToken"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-else-if="!useModal"
|
|
||||||
class="bg-zinc-950/30 flex items-center justify-center"
|
|
||||||
>
|
|
||||||
<!-- <p class="uppercase text-sm font-display text-zinc-700 font-bold">
|
<!-- <p class="uppercase text-sm font-display text-zinc-700 font-bold">
|
||||||
{{ $t("setup.noPage") }}
|
{{ $t("setup.noPage") }}
|
||||||
</p> -->
|
</p> -->
|
||||||
<img
|
<img src="/wallpapers/signin.jpg" class="inset-0 h-full w-full object-cover" alt="" preload />
|
||||||
src="/wallpapers/signin.jpg"
|
|
||||||
class="inset-0 h-full w-full object-cover"
|
|
||||||
alt=""
|
|
||||||
preload
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -102,25 +66,17 @@
|
|||||||
<div class="fixed inset-0 bg-zinc-900/75 transition-opacity" />
|
<div class="fixed inset-0 bg-zinc-900/75 transition-opacity" />
|
||||||
|
|
||||||
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||||
|
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<div
|
<div
|
||||||
class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0"
|
class="relative transform overflow-hidden rounded-lg bg-zinc-900 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm">
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="relative transform overflow-hidden rounded-lg bg-zinc-900 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<component
|
<component :is="actions[currentAction].page" v-model="actionsComplete[currentAction]"
|
||||||
:is="actions[currentAction].page"
|
:token="bearerToken" />
|
||||||
v-model="actionsComplete[currentAction]"
|
|
||||||
:token="bearerToken"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 sm:mt-6 p-4">
|
<div class="mt-5 sm:mt-6 p-4">
|
||||||
<button
|
<button type="button"
|
||||||
type="button"
|
|
||||||
class="inline-flex w-full justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-blue-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
class="inline-flex w-full justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-blue-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||||
@click="currentAction = -1"
|
@click="currentAction = -1">
|
||||||
>
|
|
||||||
{{ $t("common.close") }}
|
{{ $t("common.close") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -164,7 +120,7 @@ if (!token)
|
|||||||
});
|
});
|
||||||
const bearerToken = `Bearer ${token}`;
|
const bearerToken = `Bearer ${token}`;
|
||||||
|
|
||||||
const allowed = await $dropFetch("/api/v1/admin", {
|
const allowed = await $dropFetch("/api/v1/admin/setup", {
|
||||||
headers: { Authorization: bearerToken },
|
headers: { Authorization: bearerToken },
|
||||||
});
|
});
|
||||||
if (!allowed)
|
if (!allowed)
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we are an admin/system
|
* Check if we are a setup token
|
||||||
*/
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const allowed = await aclManager.allowSystemACL(h3, []);
|
const allowed = await aclManager.allowSystemACL(h3, ["setup"]);
|
||||||
if (!allowed) return false;
|
if (!allowed) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -1,5 +1,8 @@
|
|||||||
import authManager from "~/server/internal/auth";
|
import authManager from "~/server/internal/auth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch public authentication provider mechanisms
|
||||||
|
*/
|
||||||
export default defineEventHandler(() => {
|
export default defineEventHandler(() => {
|
||||||
return authManager.getEnabledAuthProviders();
|
return authManager.getEnabledAuthProviders();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,6 +15,9 @@ const signinValidator = type({
|
|||||||
"rememberMe?": "boolean | undefined",
|
"rememberMe?": "boolean | undefined",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign in as a session using the "Simple" authentication mechanism. Not recommended for third-party applications.
|
||||||
|
*/
|
||||||
export default defineEventHandler<{
|
export default defineEventHandler<{
|
||||||
body: typeof signinValidator.infer;
|
body: typeof signinValidator.infer;
|
||||||
}>(async (h3) => {
|
}>(async (h3) => {
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
import taskHandler from "~/server/internal/tasks";
|
import taskHandler from "~/server/internal/tasks";
|
||||||
import authManager from "~/server/internal/auth";
|
import authManager from "~/server/internal/auth";
|
||||||
|
import { ArkErrors, type } from "arktype";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const Query = type({
|
||||||
|
id: "string",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch invitation details for pre-filling
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ query: typeof Query.infer }>(async (h3) => {
|
||||||
const t = await useTranslation(h3);
|
const t = await useTranslation(h3);
|
||||||
|
|
||||||
if (!authManager.getAuthProviders().Simple)
|
if (!authManager.getAuthProviders().Simple)
|
||||||
@ -11,13 +19,13 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: t("errors.auth.method.signinDisabled"),
|
statusMessage: t("errors.auth.method.signinDisabled"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const query = getQuery(h3);
|
const query = Query(getQuery(h3));
|
||||||
const id = query.id?.toString();
|
if (query instanceof ArkErrors)
|
||||||
if (!id)
|
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
statusMessage: t("errors.auth.inviteIdRequired"),
|
statusMessage: "Invalid query: " + query.summary,
|
||||||
});
|
});
|
||||||
|
const id = query.id;
|
||||||
taskHandler.runTaskGroupByName("cleanup:invitations");
|
taskHandler.runTaskGroupByName("cleanup:invitations");
|
||||||
|
|
||||||
const invitation = await prisma.invitation.findUnique({ where: { id: id } });
|
const invitation = await prisma.invitation.findUnique({ where: { id: id } });
|
||||||
|
|||||||
@ -18,6 +18,9 @@ const CreateUserValidator = SharedRegisterValidator.and({
|
|||||||
"displayName?": "string | undefined",
|
"displayName?": "string | undefined",
|
||||||
}).configure(throwingArktype);
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create user from invitation
|
||||||
|
*/
|
||||||
export default defineEventHandler<{
|
export default defineEventHandler<{
|
||||||
body: typeof CreateUserValidator.infer;
|
body: typeof CreateUserValidator.infer;
|
||||||
}>(async (h3) => {
|
}>(async (h3) => {
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const RemoveEntry = type({
|
||||||
|
id: "string",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove entry from collection
|
||||||
|
* @param id Collection ID
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof RemoveEntry.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:remove"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:remove"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -15,10 +26,8 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "ID required in route params",
|
statusMessage: "ID required in route params",
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, RemoveEntry);
|
||||||
const gameId = body.id;
|
const gameId = body.id;
|
||||||
if (!gameId)
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Game ID required" });
|
|
||||||
|
|
||||||
const successful = await userLibraryManager.collectionRemove(
|
const successful = await userLibraryManager.collectionRemove(
|
||||||
gameId,
|
gameId,
|
||||||
@ -31,4 +40,5 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "Collection not found",
|
statusMessage: "Collection not found",
|
||||||
});
|
});
|
||||||
return {};
|
return {};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const AddEntry = type({
|
||||||
|
id: "string",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add game to collection
|
||||||
|
* @param id Collection ID
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof AddEntry.infer }>(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:add"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:add"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -15,10 +25,8 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "ID required in route params",
|
statusMessage: "ID required in route params",
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, AddEntry);
|
||||||
const gameId = body.id;
|
const gameId = body.id;
|
||||||
if (!gameId)
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Game ID required" });
|
|
||||||
|
|
||||||
return await userLibraryManager.collectionAdd(gameId, id, userId);
|
return await userLibraryManager.collectionAdd(gameId, id, userId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a collection
|
||||||
|
* @param id Collection ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:delete"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:delete"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch collection by ID
|
||||||
|
* @param id Collection ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const DeleteEntry = type({
|
||||||
|
id: "string",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove game from user library
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof DeleteEntry.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["library:remove"]);
|
const userId = await aclManager.getUserIdACL(h3, ["library:remove"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -9,12 +19,11 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "Requires authentication",
|
statusMessage: "Requires authentication",
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, DeleteEntry);
|
||||||
|
|
||||||
const gameId = body.id;
|
const gameId = body.id;
|
||||||
if (!gameId)
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Game ID required" });
|
|
||||||
|
|
||||||
await userLibraryManager.libraryRemove(gameId, userId);
|
await userLibraryManager.libraryRemove(gameId, userId);
|
||||||
return {};
|
return {};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const AddGame = type({
|
||||||
|
id: "string",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add game to user library
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof AddGame.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["library:add"]);
|
const userId = await aclManager.getUserIdACL(h3, ["library:add"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -9,12 +19,11 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "Requires authentication",
|
statusMessage: "Requires authentication",
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, AddGame);
|
||||||
const gameId = body.id;
|
const gameId = body.id;
|
||||||
if (!gameId)
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Game ID required" });
|
|
||||||
|
|
||||||
// Add the game to the default collection
|
// Add the game to the default collection
|
||||||
await userLibraryManager.libraryAdd(gameId, userId);
|
await userLibraryManager.libraryAdd(gameId, userId);
|
||||||
return {};
|
return {};
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch user library
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all collections
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
|
|||||||
@ -1,20 +1,28 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import userLibraryManager from "~/server/internal/userlibrary";
|
import userLibraryManager from "~/server/internal/userlibrary";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const CreateCollection = type({
|
||||||
|
name: "string",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
export default defineEventHandler<{ body: typeof CreateCollection.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["collections:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, CreateCollection);
|
||||||
|
|
||||||
const name = body.name;
|
const name = body.name;
|
||||||
if (!name)
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Requires name" });
|
|
||||||
|
|
||||||
// Create the collection using the manager
|
// Create the collection using the manager
|
||||||
const newCollection = await userLibraryManager.collectionCreate(name, userId);
|
const newCollection = await userLibraryManager.collectionCreate(
|
||||||
|
name,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
return newCollection;
|
return newCollection;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch company by ID
|
||||||
|
* @param id Company ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const companyId = getRouterParam(h3, "id");
|
const companyId = getRouterParam(h3, "id")!;
|
||||||
if (!companyId)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Missing gameId in route params (somehow...?)",
|
|
||||||
});
|
|
||||||
|
|
||||||
const company = await prisma.company.findUnique({
|
const company = await prisma.company.findUnique({
|
||||||
where: { id: companyId },
|
where: { id: companyId },
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch game by ID
|
||||||
|
* @param id Game ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const gameId = getRouterParam(h3, "id");
|
const gameId = getRouterParam(h3, "id")!;
|
||||||
if (!gameId)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Missing gameId in route params (somehow...?)",
|
|
||||||
});
|
|
||||||
|
|
||||||
const game = await prisma.game.findUnique({
|
const game = await prisma.game.findUnique({
|
||||||
where: { id: gameId },
|
where: { id: gameId },
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { systemConfig } from "~/server/internal/config/sys-conf";
|
import { systemConfig } from "~/server/internal/config/sys-conf";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch instance information
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (_h3) => {
|
export default defineEventHandler(async (_h3) => {
|
||||||
return {
|
return {
|
||||||
appName: "Drop",
|
appName: "Drop",
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import { defineEventHandler, createError } from "h3";
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import newsManager from "~/server/internal/news";
|
import newsManager from "~/server/internal/news";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch news article by ID
|
||||||
|
* @param id Article ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["news:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["news:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
@ -10,12 +14,7 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "Requires authentication",
|
statusMessage: "Requires authentication",
|
||||||
});
|
});
|
||||||
|
|
||||||
const id = h3.context.params?.id;
|
const id = getRouterParam(h3, "id")!;
|
||||||
if (!id)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Missing news ID",
|
|
||||||
});
|
|
||||||
|
|
||||||
const news = await newsManager.fetchById(id);
|
const news = await newsManager.fetchById(id);
|
||||||
if (!news)
|
if (!news)
|
||||||
|
|||||||
@ -1,8 +1,21 @@
|
|||||||
|
import { ArkErrors, type } from "arktype";
|
||||||
import { defineEventHandler, getQuery } from "h3";
|
import { defineEventHandler, getQuery } from "h3";
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import newsManager from "~/server/internal/news";
|
import newsManager from "~/server/internal/news";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const NewsFetch = type({
|
||||||
|
"order?": "'asc' | 'desc'",
|
||||||
|
"tags?": "string[]",
|
||||||
|
"limit?": "string.numeric.parse",
|
||||||
|
"skip?": "string.numeric.parse",
|
||||||
|
"search?": "string",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch instance news articles
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ query: typeof NewsFetch.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["news:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["news:read"]);
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({
|
throw createError({
|
||||||
@ -10,28 +23,22 @@ export default defineEventHandler(async (h3) => {
|
|||||||
statusMessage: "Requires authentication",
|
statusMessage: "Requires authentication",
|
||||||
});
|
});
|
||||||
|
|
||||||
const query = getQuery(h3);
|
const query = NewsFetch(getQuery(h3));
|
||||||
|
if (query instanceof ArkErrors)
|
||||||
|
throw createError({ statusCode: 400, statusMessage: query.summary });
|
||||||
|
|
||||||
const orderBy = query.order as "asc" | "desc";
|
const orderBy = query.order;
|
||||||
if (orderBy) {
|
const tags = query.tags;
|
||||||
if (typeof orderBy !== "string" || !["asc", "desc"].includes(orderBy))
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Invalid order" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const tags = query.tags as string[] | undefined;
|
|
||||||
if (tags) {
|
|
||||||
if (typeof tags !== "object" || !Array.isArray(tags))
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "Invalid tags" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
take: parseInt(query.limit as string),
|
take: Math.min(query.limit ?? 10, 10),
|
||||||
skip: parseInt(query.skip as string),
|
skip: query.skip ?? 0,
|
||||||
orderBy: orderBy,
|
orderBy: orderBy,
|
||||||
...(tags && { tags: tags.map((e) => e.toString()) }),
|
...(tags && { tags: tags.map((e) => e.toString()) }),
|
||||||
search: query.search as string,
|
search: query.search,
|
||||||
};
|
};
|
||||||
|
|
||||||
const news = await newsManager.fetch(options);
|
const news = await newsManager.fetch(options);
|
||||||
return news;
|
return news;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete notification.
|
||||||
|
* @param id Notification ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["notifications:delete"]);
|
const userId = await aclManager.getUserIdACL(h3, ["notifications:delete"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch notification by ID
|
||||||
|
* @param id Notification ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark notification as read
|
||||||
|
* @param id Notification ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
|
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all notifications for this token
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark all notifications as read
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
|
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -6,6 +6,11 @@ import { logger } from "~/server/internal/logging";
|
|||||||
// Peer ID to user ID
|
// Peer ID to user ID
|
||||||
const socketSessions = new Map<string, string>();
|
const socketSessions = new Map<string, string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a WebSocket to listen for notification pushes.
|
||||||
|
*
|
||||||
|
* Sends "unauthenticated" if authentication fails, otherwise JSON notifications.
|
||||||
|
*/
|
||||||
export default defineWebSocketHandler({
|
export default defineWebSocketHandler({
|
||||||
async open(peer) {
|
async open(peer) {
|
||||||
const h3 = { headers: peer.request?.headers ?? new Headers() };
|
const h3 = { headers: peer.request?.headers ?? new Headers() };
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import objectHandler from "~/server/internal/objects";
|
import objectHandler from "~/server/internal/objects";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete object
|
||||||
|
* @param id Object ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const unsafeId = getRouterParam(h3, "id");
|
const unsafeId = getRouterParam(h3, "id");
|
||||||
if (!unsafeId)
|
if (!unsafeId)
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import objectHandler from "~/server/internal/objects";
|
import objectHandler from "~/server/internal/objects";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch object. Sets a lot of caching headers, recommended to use them.
|
||||||
|
* @param id Object ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const unsafeId = getRouterParam(h3, "id");
|
const unsafeId = getRouterParam(h3, "id");
|
||||||
if (!unsafeId)
|
if (!unsafeId)
|
||||||
|
|||||||
@ -2,7 +2,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import objectHandler from "~/server/internal/objects";
|
import objectHandler from "~/server/internal/objects";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
// this request method is purely used by the browser to check if etag values are still valid
|
/**
|
||||||
|
* Check if object has changed (etag/browser caching)
|
||||||
|
* @param id Object ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const unsafeId = getRouterParam(h3, "id");
|
const unsafeId = getRouterParam(h3, "id");
|
||||||
if (!unsafeId)
|
if (!unsafeId)
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import objectHandler from "~/server/internal/objects";
|
import objectHandler from "~/server/internal/objects";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload and overwrite object. Takes raw binary data (`application/octet-stream`)
|
||||||
|
* @param id Object ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const unsafeId = getRouterParam(h3, "id");
|
const unsafeId = getRouterParam(h3, "id");
|
||||||
if (!unsafeId)
|
if (!unsafeId)
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
// get a specific screenshot
|
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import screenshotManager from "~/server/internal/screenshots";
|
import screenshotManager from "~/server/internal/screenshots";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete screenshot by ID
|
||||||
|
* @param id Screenshot ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["screenshots:delete"]);
|
const userId = await aclManager.getUserIdACL(h3, ["screenshots:delete"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -3,6 +3,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import screenshotManager from "~/server/internal/screenshots";
|
import screenshotManager from "~/server/internal/screenshots";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch screenshot by ID. Use `/api/v1/object/:id` to actually fetch screenshot image data.
|
||||||
|
* @param id Screenshot ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
// get all user screenshots by game
|
|
||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import screenshotManager from "~/server/internal/screenshots";
|
import screenshotManager from "~/server/internal/screenshots";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all screenshots for game
|
||||||
|
* @param id Game ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -3,8 +3,10 @@ import aclManager from "~/server/internal/acls";
|
|||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
import screenshotManager from "~/server/internal/screenshots";
|
import screenshotManager from "~/server/internal/screenshots";
|
||||||
|
|
||||||
// TODO: make defineClientEventHandler instead?
|
/**
|
||||||
// only clients will be upload screenshots yea??
|
* Upload screenshot by game. Subject to change, will likely become a client route. Takes raw upload (`application/octet-stream`)
|
||||||
|
* @param id Game ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["screenshots:new"]);
|
const userId = await aclManager.getUserIdACL(h3, ["screenshots:new"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import screenshotManager from "~/server/internal/screenshots";
|
import screenshotManager from "~/server/internal/screenshots";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all screenshots
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["screenshots:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import { applicationSettings } from "~/server/internal/config/application-configuration";
|
import { applicationSettings } from "~/server/internal/config/application-configuration";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch system settings
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const allowed = await aclManager.getUserACL(h3, ["settings:read"]);
|
const allowed = await aclManager.getUserACL(h3, ["settings:read"]);
|
||||||
if (!allowed) throw createError({ statusCode: 403 });
|
if (!allowed) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -2,6 +2,9 @@ import { APITokenMode } from "~/prisma/client/enums";
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete setup, and delete setup token.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const allowed = await aclManager.allowSystemACL(h3, ["setup"]);
|
const allowed = await aclManager.allowSystemACL(h3, ["setup"]);
|
||||||
if (!allowed)
|
if (!allowed)
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all featured games. Used for store carousel.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -21,7 +21,11 @@ const StoreRead = type({
|
|||||||
sort: "'default' | 'newest' | 'recent' = 'default'",
|
sort: "'default' | 'newest' | 'recent' = 'default'",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
/**
|
||||||
|
* Store endpoint. Filter games with pagination. Used for all "store views".
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ query: typeof StoreRead.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
@ -119,4 +123,5 @@ export default defineEventHandler(async (h3) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return { results, count };
|
return { results, count };
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all game tags.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch tag by ID
|
||||||
|
* @param id Tag ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["store:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const tagId = getRouterParam(h3, "id");
|
const tagId = getRouterParam(h3, "id")!;
|
||||||
if (!tagId)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Missing gameId in route params (somehow...?)",
|
|
||||||
});
|
|
||||||
|
|
||||||
const tag = await prisma.gameTag.findUnique({
|
const tag = await prisma.gameTag.findUnique({
|
||||||
where: { id: tagId },
|
where: { id: tagId },
|
||||||
|
|||||||
@ -5,6 +5,15 @@ import type { MinimumRequestObject } from "~/server/h3";
|
|||||||
// ID to admin
|
// ID to admin
|
||||||
const socketHeaders = new Map<string, MinimumRequestObject>();
|
const socketHeaders = new Map<string, MinimumRequestObject>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket to listen to task updates.
|
||||||
|
*
|
||||||
|
* Sends "unauthenticated" if authentication failed.
|
||||||
|
*
|
||||||
|
* Use `connect/:taskId` to subscribe to a task.
|
||||||
|
*
|
||||||
|
* Sends JSON tasks for all tasks subscribed.
|
||||||
|
*/
|
||||||
export default defineWebSocketHandler({
|
export default defineWebSocketHandler({
|
||||||
async open(peer) {
|
async open(peer) {
|
||||||
const request = peer.request;
|
const request = peer.request;
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import clientHandler from "~/server/internal/clients/handler";
|
import clientHandler from "~/server/internal/clients/handler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke client
|
||||||
|
* @param id Client ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["clients:revoke"]);
|
const userId = await aclManager.getUserIdACL(h3, ["clients:revoke"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const clientId = getRouterParam(h3, "id");
|
const clientId = getRouterParam(h3, "id")!;
|
||||||
if (!clientId)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Client ID missing in route params",
|
|
||||||
});
|
|
||||||
|
|
||||||
await clientHandler.removeClient(clientId);
|
await clientHandler.removeClient(clientId);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all clients connected to this account
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, ["clients:read"]);
|
const userId = await aclManager.getUserIdACL(h3, ["clients:read"]);
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch user.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const user = await aclManager.getUserACL(h3, ["read"]);
|
const user = await aclManager.getUserACL(h3, ["read"]);
|
||||||
return user ?? null; // Need to specifically return null
|
return user ?? null; // Need to specifically return null
|
||||||
|
|||||||
@ -2,16 +2,15 @@ import { APITokenMode } from "~/prisma/client/enums";
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke token
|
||||||
|
* @param id Token ID
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const id = h3.context.params?.id;
|
const id = getRouterParam(h3, "id")!;
|
||||||
if (!id)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "No id in router params",
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleted = await prisma.aPIToken.delete({
|
const deleted = await prisma.aPIToken.delete({
|
||||||
where: { id: id, userId: userId, mode: APITokenMode.User },
|
where: { id: id, userId: userId, mode: APITokenMode.User },
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import { userACLDescriptions } from "~/server/internal/acls/descriptions";
|
import { userACLDescriptions } from "~/server/internal/acls/descriptions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch ACL descriptions.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -2,6 +2,9 @@ import { APITokenMode } from "~/prisma/client/enums";
|
|||||||
import aclManager from "~/server/internal/acls";
|
import aclManager from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all API tokens for this account.
|
||||||
|
*/
|
||||||
export default defineEventHandler(async (h3) => {
|
export default defineEventHandler(async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -1,29 +1,26 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
import { APITokenMode } from "~/prisma/client/enums";
|
import { APITokenMode } from "~/prisma/client/enums";
|
||||||
|
import { readDropValidatedBody, throwingArktype } from "~/server/arktype";
|
||||||
import aclManager, { userACLs } from "~/server/internal/acls";
|
import aclManager, { userACLs } from "~/server/internal/acls";
|
||||||
import prisma from "~/server/internal/db/database";
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
const CreateToken = type({
|
||||||
|
name: "string",
|
||||||
|
acls: "string[] > 0",
|
||||||
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof CreateToken.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
const userId = await aclManager.getUserIdACL(h3, []); // No ACLs only allows session authentication
|
||||||
if (!userId) throw createError({ statusCode: 403 });
|
if (!userId) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
const body = await readBody(h3);
|
const body = await readDropValidatedBody(h3, CreateToken);
|
||||||
const name: string = body.name;
|
const name: string = body.name;
|
||||||
const acls: string[] = body.acls;
|
const acls: string[] = body.acls;
|
||||||
|
|
||||||
if (!name || typeof name !== "string")
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Token name required",
|
|
||||||
});
|
|
||||||
if (!acls || !Array.isArray(acls))
|
|
||||||
throw createError({ statusCode: 400, statusMessage: "ACLs required" });
|
|
||||||
|
|
||||||
if (acls.length == 0)
|
|
||||||
throw createError({
|
|
||||||
statusCode: 400,
|
|
||||||
statusMessage: "Token requires more than zero ACLs",
|
|
||||||
});
|
|
||||||
|
|
||||||
const invalidACLs = acls.filter(
|
const invalidACLs = acls.filter(
|
||||||
(e) => userACLs.findIndex((v) => e == v) == -1,
|
(e) => userACLs.findIndex((v) => e == v) == -1,
|
||||||
);
|
);
|
||||||
@ -43,4 +40,5 @@ export default defineEventHandler(async (h3) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -11,7 +11,13 @@ const GetChunk = type({
|
|||||||
}).array(),
|
}).array(),
|
||||||
}).configure(throwingArktype);
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
export default defineEventHandler(async (h3) => {
|
/**
|
||||||
|
* Part of v2 download API. Intended to be client-only.
|
||||||
|
*
|
||||||
|
* Returns raw stream of all files requested, in order.
|
||||||
|
*/
|
||||||
|
export default defineEventHandler<{ body: typeof GetChunk.infer }>(
|
||||||
|
async (h3) => {
|
||||||
const body = await readDropValidatedBody(h3, GetChunk);
|
const body = await readDropValidatedBody(h3, GetChunk);
|
||||||
|
|
||||||
const context = await contextManager.fetchContext(body.context);
|
const context = await contextManager.fetchContext(body.context);
|
||||||
@ -70,4 +76,5 @@ export default defineEventHandler(async (h3) => {
|
|||||||
await h3.node.res.end();
|
await h3.node.res.end();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -8,7 +8,11 @@ const CreateContext = type({
|
|||||||
version: "string",
|
version: "string",
|
||||||
}).configure(throwingArktype);
|
}).configure(throwingArktype);
|
||||||
|
|
||||||
export default defineClientEventHandler(async (h3) => {
|
/**
|
||||||
|
* 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);
|
const body = await readDropValidatedBody(h3, CreateContext);
|
||||||
|
|
||||||
const context = await contextManager.createContext(body.game, body.version);
|
const context = await contextManager.createContext(body.game, body.version);
|
||||||
@ -19,4 +23,5 @@ export default defineClientEventHandler(async (h3) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return { context };
|
return { context };
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@ -184,9 +184,6 @@ class ACLManager {
|
|||||||
if (!token) return false;
|
if (!token) return false;
|
||||||
if (token.mode != APITokenMode.System) return false;
|
if (token.mode != APITokenMode.System) return false;
|
||||||
|
|
||||||
// If empty, we just want to check we are an admin *at all*, not specific ACLs
|
|
||||||
if (acls.length == 0) return true;
|
|
||||||
|
|
||||||
for (const acl of acls) {
|
for (const acl of acls) {
|
||||||
const tokenACLIndex = token.acls.findIndex((e) => e == acl);
|
const tokenACLIndex = token.acls.findIndex((e) => e == acl);
|
||||||
if (tokenACLIndex != -1) return true;
|
if (tokenACLIndex != -1) return true;
|
||||||
|
|||||||
@ -17,7 +17,9 @@ type ClientUtils = {
|
|||||||
|
|
||||||
const NONCE_LENIENCE = 30_000;
|
const NONCE_LENIENCE = 30_000;
|
||||||
|
|
||||||
export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
|
export function defineClientEventHandler<R extends EventHandlerRequest = object, K = unknown>(
|
||||||
|
handler: (h3: H3Event<R>, utils: ClientUtils) => Promise<K> | K,
|
||||||
|
) {
|
||||||
return defineEventHandler(async (h3) => {
|
return defineEventHandler(async (h3) => {
|
||||||
const header = getHeader(h3, "Authorization");
|
const header = getHeader(h3, "Authorization");
|
||||||
if (!header) throw createError({ statusCode: 403 });
|
if (!header) throw createError({ statusCode: 403 });
|
||||||
|
|||||||
@ -46,9 +46,9 @@ class NewsManager {
|
|||||||
options: {
|
options: {
|
||||||
take?: number;
|
take?: number;
|
||||||
skip?: number;
|
skip?: number;
|
||||||
orderBy?: "asc" | "desc";
|
orderBy?: "asc" | "desc" | undefined;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
search?: string;
|
search?: string | undefined;
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
return await prisma.article.findMany({
|
return await prisma.article.findMany({
|
||||||
|
|||||||
Reference in New Issue
Block a user