mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-13 16:22:39 +10:00
feat: make internal objectbackend methods private
This commit is contained in:
@ -3,9 +3,7 @@ import prisma from "~/server/internal/db/database";
|
||||
import objectHandler from "~/server/internal/objects";
|
||||
|
||||
export default defineEventHandler(async (h3) => {
|
||||
const allowed = await aclManager.allowSystemACL(h3, [
|
||||
"game:image:delete",
|
||||
]);
|
||||
const allowed = await aclManager.allowSystemACL(h3, ["game:image:delete"]);
|
||||
if (!allowed) throw createError({ statusCode: 403 });
|
||||
|
||||
const body = await readBody(h3);
|
||||
@ -37,7 +35,7 @@ export default defineEventHandler(async (h3) => {
|
||||
throw createError({ statusCode: 400, statusMessage: "Image not found" });
|
||||
|
||||
game.mImageLibrary.splice(imageIndex, 1);
|
||||
await objectHandler.delete(imageId);
|
||||
await objectHandler.deleteWithPermission(imageId);
|
||||
|
||||
if (game.mBannerId === imageId) {
|
||||
game.mBannerId = game.mImageLibrary[0];
|
||||
|
||||
@ -13,7 +13,10 @@ export default defineEventHandler(async (h3) => {
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
|
||||
const etagRequestValue = h3.headers.get("If-None-Match");
|
||||
const etagActualValue = await objectHandler.fetchHash(id);
|
||||
const etagActualValue = await objectHandler.fetchHashWithWithPermissions(
|
||||
id,
|
||||
userId
|
||||
);
|
||||
if (etagRequestValue !== null && etagActualValue === etagRequestValue) {
|
||||
// would compare if etag is valid, but objects should never change
|
||||
setResponseStatus(h3, 304);
|
||||
|
||||
@ -14,7 +14,10 @@ export default defineEventHandler(async (h3) => {
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
|
||||
const etagRequestValue = h3.headers.get("If-None-Match");
|
||||
const etagActualValue = await objectHandler.fetchHash(id);
|
||||
const etagActualValue = await objectHandler.fetchHashWithWithPermissions(
|
||||
id,
|
||||
userId
|
||||
);
|
||||
if (etagRequestValue !== null && etagActualValue === etagRequestValue) {
|
||||
// would compare if etag is valid, but objects should never change
|
||||
setResponseStatus(h3, 304);
|
||||
|
||||
@ -129,7 +129,7 @@ class NewsManager {
|
||||
where: { id },
|
||||
});
|
||||
if (article.image) {
|
||||
return await objectHandler.delete(article.image);
|
||||
return await objectHandler.deleteWithPermission(article.image);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import {
|
||||
Object,
|
||||
ObjectBackend,
|
||||
ObjectMetadata,
|
||||
ObjectReference,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { FsObjectBackend } from "./fsBackend";
|
||||
export const objectHandler = new FsObjectBackend();
|
||||
export default objectHandler
|
||||
import { ObjectHandler } from "./objectHandler";
|
||||
|
||||
export const objectHandler = new ObjectHandler(new FsObjectBackend());
|
||||
export default objectHandler;
|
||||
|
||||
@ -49,21 +49,29 @@ export abstract class ObjectBackend {
|
||||
abstract create(
|
||||
id: string,
|
||||
source: Source,
|
||||
metadata: ObjectMetadata,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<ObjectReference | undefined>;
|
||||
abstract createWithWriteStream(
|
||||
id: string,
|
||||
metadata: ObjectMetadata,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<Writable | undefined>;
|
||||
abstract delete(id: ObjectReference): Promise<boolean>;
|
||||
abstract fetchMetadata(
|
||||
id: ObjectReference,
|
||||
id: ObjectReference
|
||||
): Promise<ObjectMetadata | undefined>;
|
||||
abstract writeMetadata(
|
||||
id: ObjectReference,
|
||||
metadata: ObjectMetadata,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<boolean>;
|
||||
abstract fetchHash(id: ObjectReference): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export class ObjectHandler {
|
||||
private backend: ObjectBackend;
|
||||
|
||||
constructor(backend: ObjectBackend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
private async fetchMimeType(source: Source) {
|
||||
if (source instanceof ReadableStream) {
|
||||
@ -87,13 +95,13 @@ export abstract class ObjectBackend {
|
||||
id: string,
|
||||
sourceFetcher: () => Promise<Source>,
|
||||
metadata: { [key: string]: string },
|
||||
permissions: Array<string>,
|
||||
permissions: Array<string>
|
||||
) {
|
||||
const { source, mime } = await this.fetchMimeType(await sourceFetcher());
|
||||
if (!mime)
|
||||
throw new Error("Unable to calculate MIME type - is the source empty?");
|
||||
|
||||
await this.create(id, source, {
|
||||
await this.backend.create(id, source, {
|
||||
permissions,
|
||||
userMetadata: metadata,
|
||||
mime,
|
||||
@ -103,9 +111,9 @@ export abstract class ObjectBackend {
|
||||
async createWithStream(
|
||||
id: string,
|
||||
metadata: { [key: string]: string },
|
||||
permissions: Array<string>,
|
||||
permissions: Array<string>
|
||||
) {
|
||||
return this.createWithWriteStream(id, {
|
||||
return this.backend.createWithWriteStream(id, {
|
||||
permissions,
|
||||
userMetadata: metadata,
|
||||
mime: "application/octet-stream",
|
||||
@ -119,7 +127,7 @@ export abstract class ObjectBackend {
|
||||
* @returns
|
||||
*/
|
||||
async fetchWithPermissions(id: ObjectReference, userId?: string) {
|
||||
const metadata = await this.fetchMetadata(id);
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return;
|
||||
|
||||
// We only need one permission, so find instead of filter is faster
|
||||
@ -137,7 +145,7 @@ export abstract class ObjectBackend {
|
||||
|
||||
// Because any permission can be read or up, we automatically know we can read this object
|
||||
// So just straight return the object
|
||||
const source = await this.fetch(id);
|
||||
const source = await this.backend.fetch(id);
|
||||
if (!source) return undefined;
|
||||
const object: Object = {
|
||||
data: source,
|
||||
@ -146,6 +154,28 @@ export abstract class ObjectBackend {
|
||||
return object;
|
||||
}
|
||||
|
||||
async fetchHashWithWithPermissions(id: ObjectReference, userId?: string) {
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return;
|
||||
|
||||
// We only need one permission, so find instead of filter is faster
|
||||
const myPermissions = metadata.permissions.find((e) => {
|
||||
if (userId !== undefined && e.startsWith(userId)) return true;
|
||||
if (userId !== undefined && e.startsWith("internal")) return true;
|
||||
if (e.startsWith("anonymous")) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!myPermissions) {
|
||||
// We do not have access to this object
|
||||
return;
|
||||
}
|
||||
|
||||
// Because any permission can be read or up, we automatically know we can read this object
|
||||
// So just straight return the object
|
||||
return await this.backend.fetchHash(id);
|
||||
}
|
||||
|
||||
// If we need to fetch a remote resource, it doesn't make sense
|
||||
// to immediately fetch the object, *then* check permissions.
|
||||
// Instead the caller can pass a simple anonymous funciton, like
|
||||
@ -154,9 +184,9 @@ export abstract class ObjectBackend {
|
||||
async writeWithPermissions(
|
||||
id: ObjectReference,
|
||||
sourceFetcher: () => Promise<Source>,
|
||||
userId?: string,
|
||||
userId?: string
|
||||
) {
|
||||
const metadata = await this.fetchMetadata(id);
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return false;
|
||||
|
||||
const myPermissions = metadata.permissions
|
||||
@ -178,13 +208,13 @@ export abstract class ObjectBackend {
|
||||
if (!hasPermission) return false;
|
||||
|
||||
const source = await sourceFetcher();
|
||||
const result = await this.write(id, source);
|
||||
const result = await this.backend.write(id, source);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async deleteWithPermission(id: ObjectReference, userId?: string) {
|
||||
const metadata = await this.fetchMetadata(id);
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return false;
|
||||
|
||||
const myPermissions = metadata.permissions
|
||||
@ -205,7 +235,7 @@ export abstract class ObjectBackend {
|
||||
|
||||
if (!hasPermission) return false;
|
||||
|
||||
const result = await this.delete(id);
|
||||
const result = await this.backend.delete(id);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ class SaveManager {
|
||||
index: number,
|
||||
objectId: string
|
||||
) {
|
||||
await objectHandler.delete(objectId);
|
||||
await objectHandler.deleteWithPermission(objectId, userId);
|
||||
}
|
||||
|
||||
async pushSave(
|
||||
@ -62,7 +62,7 @@ class SaveManager {
|
||||
await Promise.all([hashPromise, uploadStream]);
|
||||
|
||||
if (!hash) {
|
||||
await objectHandler.delete(newSaveObjectId);
|
||||
await objectHandler.deleteWithPermission(newSaveObjectId, userId);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Hash failed to generate",
|
||||
|
||||
Reference in New Issue
Block a user