mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-13 16:22:39 +10:00
fix: etags and other
remove sanitize-filename because IDs are internally generated remove pulse animation on NO GAME cards add migration refactors to be inline with other stuff
This commit is contained in:
@ -129,7 +129,7 @@ class NewsManager {
|
||||
where: { id },
|
||||
});
|
||||
if (article.image) {
|
||||
return await objectHandler.deleteAsServer(article.image);
|
||||
return await objectHandler.deleteAsSystem(article.image);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import {
|
||||
Source,
|
||||
} from "./objectHandler";
|
||||
|
||||
import sanitize from "sanitize-filename";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
@ -30,12 +29,13 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
}
|
||||
|
||||
async fetch(id: ObjectReference) {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
console.log("ID: " + id);
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
if (!fs.existsSync(objectPath)) return undefined;
|
||||
return fs.createReadStream(objectPath);
|
||||
}
|
||||
async write(id: ObjectReference, source: Source): Promise<boolean> {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
if (!fs.existsSync(objectPath)) return false;
|
||||
|
||||
// remove item from cache
|
||||
@ -56,7 +56,7 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
return false;
|
||||
}
|
||||
async startWriteStream(id: ObjectReference) {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
if (!fs.existsSync(objectPath)) return undefined;
|
||||
// remove item from cache
|
||||
this.hashStore.delete(id);
|
||||
@ -67,11 +67,8 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
source: Source,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<ObjectReference | undefined> {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const metadataPath = path.join(
|
||||
this.baseMetadataPath,
|
||||
`${sanitize(id)}.json`
|
||||
);
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
|
||||
if (fs.existsSync(objectPath) || fs.existsSync(metadataPath))
|
||||
return undefined;
|
||||
|
||||
@ -87,11 +84,8 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
return id;
|
||||
}
|
||||
async createWithWriteStream(id: string, metadata: ObjectMetadata) {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const metadataPath = path.join(
|
||||
this.baseMetadataPath,
|
||||
`${sanitize(id)}.json`
|
||||
);
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
|
||||
if (fs.existsSync(objectPath) || fs.existsSync(metadataPath))
|
||||
return undefined;
|
||||
|
||||
@ -101,10 +95,12 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
// Create file so write passes
|
||||
fs.writeFileSync(objectPath, "");
|
||||
|
||||
return this.startWriteStream(id);
|
||||
const stream = await this.startWriteStream(id);
|
||||
if (!stream) throw new Error("Could not create write stream");
|
||||
return stream;
|
||||
}
|
||||
async delete(id: ObjectReference): Promise<boolean> {
|
||||
const objectPath = path.join(this.baseObjectPath, sanitize(id));
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
if (!fs.existsSync(objectPath)) return true;
|
||||
fs.rmSync(objectPath);
|
||||
// remove item from cache
|
||||
@ -114,10 +110,7 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
async fetchMetadata(
|
||||
id: ObjectReference
|
||||
): Promise<ObjectMetadata | undefined> {
|
||||
const metadataPath = path.join(
|
||||
this.baseMetadataPath,
|
||||
`${sanitize(id)}.json`
|
||||
);
|
||||
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
|
||||
if (!fs.existsSync(metadataPath)) return undefined;
|
||||
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
||||
return metadata as ObjectMetadata;
|
||||
@ -126,16 +119,13 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
id: ObjectReference,
|
||||
metadata: ObjectMetadata
|
||||
): Promise<boolean> {
|
||||
const metadataPath = path.join(
|
||||
this.baseMetadataPath,
|
||||
`${sanitize(id)}.json`
|
||||
);
|
||||
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
|
||||
if (!fs.existsSync(metadataPath)) return false;
|
||||
fs.writeFileSync(metadataPath, JSON.stringify(metadata));
|
||||
return true;
|
||||
}
|
||||
async fetchHash(id: ObjectReference): Promise<string | undefined> {
|
||||
const cacheResult = this.hashStore.get(id);
|
||||
const cacheResult = await this.hashStore.get(id);
|
||||
if (cacheResult !== undefined) return cacheResult;
|
||||
|
||||
const obj = await this.fetch(id);
|
||||
@ -147,14 +137,18 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
// hash object
|
||||
const hash = createHash("md5");
|
||||
hash.setEncoding("hex");
|
||||
obj.on("end", function () {
|
||||
hash.end();
|
||||
cache.save(id, hash.read());
|
||||
});
|
||||
|
||||
// read obj into hash
|
||||
obj.pipe(hash);
|
||||
await new Promise<void>((r) => {
|
||||
obj.on("end", function () {
|
||||
hash.end();
|
||||
cache.save(id, hash.read());
|
||||
r();
|
||||
});
|
||||
});
|
||||
|
||||
return this.hashStore.get(id);
|
||||
return await this.hashStore.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +168,7 @@ class FsHashStore {
|
||||
const cacheRes = this.cache.get(id);
|
||||
if (cacheRes !== undefined) return cacheRes;
|
||||
|
||||
const dbRes = await prisma.objectHash.findUnique({
|
||||
const objectHash = await prisma.objectHash.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
@ -182,9 +176,9 @@ class FsHashStore {
|
||||
hash: true,
|
||||
},
|
||||
});
|
||||
if (dbRes === null) return undefined;
|
||||
this.cache.set(id, dbRes.hash);
|
||||
return dbRes.hash;
|
||||
if (objectHash === null) return undefined;
|
||||
this.cache.set(id, objectHash.hash);
|
||||
return objectHash.hash;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -120,6 +120,32 @@ export class ObjectHandler {
|
||||
});
|
||||
}
|
||||
|
||||
// We only need one permission, so find instead of filter is faster
|
||||
private hasAnyPermissions(permissions: string[], userId?: string) {
|
||||
return !!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;
|
||||
});
|
||||
}
|
||||
|
||||
private fetchPermissions(permissions: string[], userId?: string) {
|
||||
return (
|
||||
permissions
|
||||
.filter((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;
|
||||
})
|
||||
// Strip IDs from permissions
|
||||
.map((e) => e.split(":").at(1))
|
||||
// Map to priority according to array
|
||||
.map((e) => ObjectPermissionPriority.findIndex((c) => c === e))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches object, but also checks if user has perms to access it
|
||||
* @param id object id
|
||||
@ -130,18 +156,7 @@ export class ObjectHandler {
|
||||
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;
|
||||
}
|
||||
if (!this.hasAnyPermissions(metadata.permissions, userId)) return;
|
||||
|
||||
// Because any permission can be read or up, we automatically know we can read this object
|
||||
// So just straight return the object
|
||||
@ -155,30 +170,11 @@ export class ObjectHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch object hash, but also checks if user has perms to access it
|
||||
* Fetch object hash. Permissions check should be done on read
|
||||
* @param id object id
|
||||
* @param userId user to check, or act as anon user
|
||||
* @returns
|
||||
*/
|
||||
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
|
||||
async fetchHash(id: ObjectReference) {
|
||||
return await this.backend.fetchHash(id);
|
||||
}
|
||||
|
||||
@ -202,21 +198,11 @@ export class ObjectHandler {
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return false;
|
||||
|
||||
const myPermissions = metadata.permissions
|
||||
.filter((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;
|
||||
})
|
||||
// Strip IDs from permissions
|
||||
.map((e) => e.split(":").at(1))
|
||||
// Map to priority according to array
|
||||
.map((e) => ObjectPermissionPriority.findIndex((c) => c === e));
|
||||
const permissions = this.fetchPermissions(metadata.permissions, userId);
|
||||
|
||||
const requiredPermissionIndex = 1;
|
||||
const hasPermission =
|
||||
myPermissions.find((e) => e >= requiredPermissionIndex) != undefined;
|
||||
permissions.find((e) => e >= requiredPermissionIndex) != undefined;
|
||||
|
||||
if (!hasPermission) return false;
|
||||
|
||||
@ -237,21 +223,11 @@ export class ObjectHandler {
|
||||
const metadata = await this.backend.fetchMetadata(id);
|
||||
if (!metadata) return false;
|
||||
|
||||
const myPermissions = metadata.permissions
|
||||
.filter((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;
|
||||
})
|
||||
// Strip IDs from permissions
|
||||
.map((e) => e.split(":").at(1))
|
||||
// Map to priority according to array
|
||||
.map((e) => ObjectPermissionPriority.findIndex((c) => c === e));
|
||||
const permissions = this.fetchPermissions(metadata.permissions, userId);
|
||||
|
||||
const requiredPermissionIndex = 2;
|
||||
const hasPermission =
|
||||
myPermissions.find((e) => e >= requiredPermissionIndex) != undefined;
|
||||
permissions.find((e) => e >= requiredPermissionIndex) != undefined;
|
||||
|
||||
if (!hasPermission) return false;
|
||||
|
||||
@ -264,7 +240,7 @@ export class ObjectHandler {
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
async deleteAsServer(id: ObjectReference) {
|
||||
async deleteAsSystem(id: ObjectReference) {
|
||||
return await this.backend.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ class SaveManager {
|
||||
await Promise.all([hashPromise, uploadStream]);
|
||||
|
||||
if (!hash) {
|
||||
await objectHandler.deleteAsServer(newSaveObjectId);
|
||||
await objectHandler.deleteAsSystem(newSaveObjectId);
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Hash failed to generate",
|
||||
|
||||
Reference in New Issue
Block a user