fix: more eslint stuff

This commit is contained in:
Huskydog9988
2025-04-15 21:10:45 -04:00
parent 8f429e1e56
commit 8e109dd562
58 changed files with 1066 additions and 1016 deletions

View File

@@ -1,11 +1,8 @@
import aclManager from "~/server/internal/acls";
import prisma from "~/server/internal/db/database";
import libraryManager from "~/server/internal/library";
export default defineEventHandler(async (h3) => {
const allowed = await aclManager.allowSystemACL(h3, [
"game:delete",
]);
const allowed = await aclManager.allowSystemACL(h3, ["game:delete"]);
if (!allowed) throw createError({ statusCode: 403 });
const query = getQuery(h3);

View File

@@ -1,4 +1,4 @@
import { defineEventHandler, createError, readBody } from "h3";
import { defineEventHandler, createError } from "h3";
import aclManager from "~/server/internal/acls";
import newsManager from "~/server/internal/news";
import { handleFileUpload } from "~/server/internal/utils/handlefileupload";
@@ -21,7 +21,7 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Failed to upload file",
});
const [imageId, options, pull, dump] = uploadResult;
const [imageId, options, pull, _dump] = uploadResult;
const title = options.title;
const description = options.description;

View File

@@ -1,4 +1,4 @@
import { AuthMec, Invitation } from "@prisma/client";
import { AuthMec } from "@prisma/client";
import prisma from "~/server/internal/db/database";
import { createHashArgon2 } from "~/server/internal/security/simple";
import * as jdenticon from "jdenticon";
@@ -63,7 +63,7 @@ export default defineEventHandler(async (h3) => {
profilePictureId,
async () => jdenticon.toPng(user.username, 256),
{},
[`internal:read`, `${userId}:read`]
[`internal:read`, `${userId}:read`],
);
const [linkMec] = await prisma.$transaction([
prisma.linkedAuthMec.create({

View File

@@ -1,3 +1 @@
export default defineEventHandler((h3) => {
})
export default defineEventHandler((_h3) => {});

View File

@@ -1,4 +1,3 @@
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import prisma from "~/server/internal/db/database";
import fs from "fs";
import path from "path";
@@ -33,7 +32,7 @@ export default defineEventHandler(async (h3) => {
const versionDir = path.join(
libraryManager.fetchLibraryPath(),
game.libraryBasePath,
versionName
versionName,
);
if (!fs.existsSync(versionDir))
throw createError({

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";
@@ -20,7 +19,7 @@ export default defineClientEventHandler(async (h3, { fetchUser }) => {
const successful = await userLibraryManager.collectionRemove(
gameId,
id,
user.id
user.id,
);
if (!successful)
throw createError({

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";

View File

@@ -1,4 +1,3 @@
import aclManager from "~/server/internal/acls";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import userLibraryManager from "~/server/internal/userlibrary";
@@ -12,6 +11,9 @@ export default defineClientEventHandler(async (h3, { fetchUser }) => {
throw createError({ statusCode: 400, statusMessage: "Requires name" });
// Create the collection using the manager
const newCollection = await userLibraryManager.collectionCreate(name, user.id);
const newCollection = await userLibraryManager.collectionCreate(
name,
user.id,
);
return newCollection;
});

View File

@@ -1,8 +1,7 @@
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import prisma from "~/server/internal/db/database";
import { DropManifest } from "~/server/internal/downloads/manifest";
export default defineClientEventHandler(async (h3, {}) => {
export default defineClientEventHandler(async (h3) => {
const query = getQuery(h3);
const id = query.id?.toString();
if (!id)
@@ -23,9 +22,9 @@ export default defineClientEventHandler(async (h3, {}) => {
const mappedVersions = versions
.map((version) => {
if (!version.dropletManifest) return undefined;
const newVersion = { ...version, dropletManifest: undefined };
// @ts-expect-error
// @ts-expect-error idk why we delete an undefined object
delete newVersion.dropletManifest;
return {
...newVersion,

View File

@@ -1,20 +1,17 @@
import { ClientCapabilities } from "@prisma/client";
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import { applicationSettings } from "~/server/internal/config/application-configuration";
import prisma from "~/server/internal/db/database";
export default defineClientEventHandler(
async (h3, { fetchClient, fetchUser }) => {
const client = await fetchClient();
if (!client.capabilities.includes(ClientCapabilities.CloudSaves))
throw createError({
statusCode: 403,
statusMessage: "Capability not allowed.",
});
export default defineClientEventHandler(async (_h3, { fetchClient }) => {
const client = await fetchClient();
if (!client.capabilities.includes(ClientCapabilities.CloudSaves))
throw createError({
statusCode: 403,
statusMessage: "Capability not allowed.",
});
const slotLimit = await applicationSettings.get("saveSlotCountLimit");
const sizeLimit = await applicationSettings.get("saveSlotSizeLimit");
const history = await applicationSettings.get("saveSlotHistoryLimit");
return { slotLimit, sizeLimit, history };
}
);
const slotLimit = await applicationSettings.get("saveSlotCountLimit");
const sizeLimit = await applicationSettings.get("saveSlotSizeLimit");
const history = await applicationSettings.get("saveSlotHistoryLimit");
return { slotLimit, sizeLimit, history };
});

View File

@@ -1,8 +1,7 @@
import { defineClientEventHandler } from "~/server/internal/clients/event-handler";
import prisma from "~/server/internal/db/database";
import userLibraryManager from "~/server/internal/userlibrary";
export default defineClientEventHandler(async (h3, { fetchUser }) => {
export default defineClientEventHandler(async (_h3, { fetchUser }) => {
const user = await fetchUser();
const library = await userLibraryManager.fetchLibrary(user.id);
return library.entries.map((e) => e.game);

View File

@@ -1,4 +1,4 @@
export default defineEventHandler((h3) => {
export default defineEventHandler((_h3) => {
return {
appName: "Drop",
};

View File

@@ -1,11 +1,9 @@
import notificationSystem from "~/server/internal/notifications";
import session from "~/server/internal/session";
import { parse as parseCookies } from "cookie-es";
import aclManager from "~/server/internal/acls";
// TODO add web socket sessions for horizontal scaling
// Peer ID to user ID
const socketSessions: { [key: string]: string } = {};
const socketSessions = new Map<string, string>();
export default defineWebSocketHandler({
async open(peer) {
@@ -25,7 +23,7 @@ export default defineWebSocketHandler({
userIds.push("system");
}
socketSessions[peer.id] = userId;
socketSessions.set(peer.id, userId);
for (const listenUserId of userIds) {
notificationSystem.listen(listenUserId, peer.id, (notification) => {
@@ -33,8 +31,8 @@ export default defineWebSocketHandler({
});
}
},
async close(peer, details) {
const userId = socketSessions[peer.id];
async close(peer, _details) {
const userId = socketSessions.get(peer.id);
if (!userId) {
console.log(`skipping websocket close for ${peer.id}`);
return;
@@ -42,6 +40,6 @@ export default defineWebSocketHandler({
notificationSystem.unlisten(userId, peer.id);
notificationSystem.unlisten("system", peer.id); // In case we were listening as 'system'
delete socketSessions[peer.id];
socketSessions.delete(peer.id);
},
});

View File

@@ -1,11 +1,9 @@
import session from "~/server/internal/session";
import taskHandler, { TaskMessage } from "~/server/internal/tasks";
import { parse as parseCookies } from "cookie-es";
import taskHandler from "~/server/internal/tasks";
import type { MinimumRequestObject } from "~/server/h3";
// TODO add web socket sessions for horizontal scaling
// ID to admin
const socketHeaders: { [key: string]: MinimumRequestObject } = {};
const socketHeaders = new Map<string, MinimumRequestObject>();
export default defineWebSocketHandler({
async open(peer) {
@@ -15,25 +13,26 @@ export default defineWebSocketHandler({
return;
}
socketHeaders[peer.id] = {
socketHeaders.set(peer.id, {
headers: request.headers ?? new Headers(),
};
});
peer.send(`connect`);
},
message(peer, message) {
if (!peer.id) return;
if (socketHeaders[peer.id] === undefined) return;
const headers = socketHeaders.get(peer.id);
if (headers === undefined) return;
const text = message.text();
if (text.startsWith("connect/")) {
const id = text.substring("connect/".length);
taskHandler.connect(peer.id, id, peer, socketHeaders[peer.id]);
taskHandler.connect(peer.id, id, peer, headers);
return;
}
},
close(peer, details) {
close(peer, _details) {
if (!peer.id) return;
if (socketHeaders[peer.id] === undefined) return;
delete socketHeaders[peer.id];
if (!socketHeaders.has(peer.id)) return;
socketHeaders.delete(peer.id);
taskHandler.disconnectAll(peer.id);
},

View File

@@ -1,6 +1,5 @@
import aclManager from "~/server/internal/acls";
import clientHandler from "~/server/internal/clients/handler";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
const userId = await aclManager.getUserIdACL(h3, ["clients:revoke"]);

View File

@@ -1,6 +1,5 @@
import { APITokenMode } from "@prisma/client";
import aclManager, { userACLs } from "~/server/internal/acls";
import { userACLDescriptions } from "~/server/internal/acls/descriptions";
import prisma from "~/server/internal/db/database";
export default defineEventHandler(async (h3) => {
@@ -25,17 +24,23 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Token requires more than zero ACLs",
});
const invalidACLs = acls.filter((e) => userACLs.findIndex((v) => e == v) == -1);
if(invalidACLs.length > 0) throw createError({statusCode: 400, statusMessage: `Invalid ACLs: ${invalidACLs.join(", ")}`});
const invalidACLs = acls.filter(
(e) => userACLs.findIndex((v) => e == v) == -1,
);
if (invalidACLs.length > 0)
throw createError({
statusCode: 400,
statusMessage: `Invalid ACLs: ${invalidACLs.join(", ")}`,
});
const token = await prisma.aPIToken.create({
data: {
mode: APITokenMode.User,
name: name,
userId: userId,
acls: acls,
}
})
return token;
const token = await prisma.aPIToken.create({
data: {
mode: APITokenMode.User,
name: name,
userId: userId,
acls: acls,
},
});
return token;
});

View File

@@ -1,5 +1,4 @@
import { APITokenMode, User } from "@prisma/client";
import { H3Context, H3Event } from "h3";
import { APITokenMode } from "@prisma/client";
import prisma from "../db/database";
import sessionHandler from "../session";
import type { MinimumRequestObject } from "~/server/h3";
@@ -98,7 +97,7 @@ class ACLManager {
if (!token) return undefined;
if (!token.userId)
throw new Error(
"No userId on user or client token - is something broken?"
"No userId on user or client token - is something broken?",
);
for (const acl of acls) {
@@ -124,7 +123,7 @@ class ACLManager {
async allowSystemACL(
request: MinimumRequestObject | undefined,
acls: SystemACL
acls: SystemACL,
) {
if (!request)
throw new Error("Native web requests not available - weird deployment?");
@@ -157,13 +156,17 @@ class ACLManager {
for (const acl of acls) {
if (acl.startsWith(userACLPrefix)) {
const rawACL = acl.substring(userACLPrefix.length);
const userId = await this.getUserIdACL(request, [rawACL as any]);
const userId = await this.getUserIdACL(request, [
rawACL as UserACL[number],
]);
if (!userId) return false;
}
if (acl.startsWith(systemACLPrefix)) {
const rawACL = acl.substring(systemACLPrefix.length);
const allowed = await this.allowSystemACL(request, [rawACL as any]);
const allowed = await this.allowSystemACL(request, [
rawACL as SystemACL[number],
]);
if (!allowed) return false;
}
}

View File

@@ -1,8 +1,5 @@
import path from "path";
import fs from "fs";
import droplet from "@drop-oss/droplet";
import type { CertificateStore} from "./ca-store";
import { fsCertificateStore } from "./ca-store";
import type { CertificateStore } from "./ca-store";
export type CertificateBundle = {
priv: string;
@@ -50,7 +47,7 @@ export class CertificateAuthority {
clientId,
clientName,
caCertificate.cert,
caCertificate.priv
caCertificate.priv,
);
const certBundle: CertificateBundle = {
priv,
@@ -65,7 +62,7 @@ export class CertificateAuthority {
async fetchClientCertificate(clientId: string) {
const isBlacklist = await this.certificateStore.checkBlacklistCertificate(
`client:${clientId}`
`client:${clientId}`,
);
if (isBlacklist) return undefined;
return await this.certificateStore.fetch(`client:${clientId}`);

View File

@@ -18,8 +18,8 @@ export const validCapabilities = Object.values(InternalClientCapability);
export type CapabilityConfiguration = {
[InternalClientCapability.PeerAPI]: { endpoints: string[] };
[InternalClientCapability.UserStatus]: {};
[InternalClientCapability.CloudSaves]: {};
[InternalClientCapability.UserStatus]: object;
[InternalClientCapability.CloudSaves]: object;
};
class CapabilityManager {
@@ -53,7 +53,7 @@ class CapabilityManager {
const serverCertificate = await ca.fetchClientCertificate("server");
if (!serverCertificate)
throw new Error(
"CA not initialised properly - server mTLS certificate not present"
"CA not initialised properly - server mTLS certificate not present",
);
const httpsAgent = new https.Agent({
key: serverCertificate.priv,
@@ -70,7 +70,9 @@ class CapabilityManager {
});
valid = true;
break;
} catch {}
} catch {
/* empty */
}
}
return valid;
@@ -81,7 +83,7 @@ class CapabilityManager {
async validateCapabilityConfiguration(
capability: InternalClientCapability,
configuration: object
configuration: object,
) {
const validationFunction = this.validationFunctions[capability];
if (!validationFunction) return false;
@@ -91,7 +93,7 @@ class CapabilityManager {
async upsertClientCapability(
capability: InternalClientCapability,
rawCapability: object,
clientId: string
clientId: string,
) {
const upsertFunctions: EnumDictionary<
InternalClientCapability,

View File

@@ -6,7 +6,7 @@ import { useCertificateAuthority } from "~/server/plugins/ca";
export type EventHandlerFunction<T> = (
h3: H3Event<EventHandlerRequest>,
utils: ClientUtils
utils: ClientUtils,
) => Promise<T> | T;
type ClientUtils = {
@@ -25,7 +25,7 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
let clientId: string;
switch (method) {
case "Debug":
case "Debug": {
if (!import.meta.dev) throw createError({ statusCode: 403 });
const client = await prisma.client.findFirst({ select: { id: true } });
if (!client)
@@ -35,7 +35,8 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
});
clientId = client.id;
break;
case "Nonce":
}
case "Nonce": {
clientId = parts[0];
const nonce = parts[1];
const signature = parts[2];
@@ -59,9 +60,8 @@ 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({
@@ -76,11 +76,13 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
statusMessage: "Invalid nonce signature.",
});
break;
default:
}
default: {
throw createError({
statusCode: 403,
statusMessage: "No authentication",
});
}
}
if (clientId === undefined)
@@ -95,7 +97,7 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
});
if (!client)
throw new Error(
"client util fetch client broke - this should NOT happen"
"client util fetch client broke - this should NOT happen",
);
return client;
}
@@ -110,7 +112,7 @@ export function defineClientEventHandler<T>(handler: EventHandlerFunction<T>) {
if (!client)
throw new Error(
"client util fetch client broke - this should NOT happen"
"client util fetch client broke - this should NOT happen",
);
return client.user;

View File

@@ -1,5 +1,4 @@
import { randomUUID } from "node:crypto";
import { CertificateBundle } from "./ca";
import prisma from "../db/database";
import type { Platform } from "@prisma/client";
import { useCertificateAuthority } from "~/server/plugins/ca";
@@ -10,25 +9,29 @@ export interface ClientMetadata {
}
export class ClientHandler {
private temporaryClientTable: {
[key: string]: {
private temporaryClientTable = new Map<
string,
{
timeout: NodeJS.Timeout;
data: ClientMetadata;
userId?: string;
authToken?: string;
};
} = {};
}
>();
async initiate(metadata: ClientMetadata) {
const clientId = randomUUID();
this.temporaryClientTable[clientId] = {
this.temporaryClientTable.set(clientId, {
data: metadata,
timeout: setTimeout(() => {
if (this.temporaryClientTable[clientId])
delete this.temporaryClientTable[clientId];
}, 1000 * 60 * 10), // 10 minutes
};
timeout: setTimeout(
() => {
if (this.temporaryClientTable.has(clientId))
this.temporaryClientTable.delete(clientId);
},
1000 * 60 * 10,
), // 10 minutes
});
return clientId;
}
@@ -38,23 +41,23 @@ export class ClientHandler {
}
async fetchClient(clientId: string) {
const entry = this.temporaryClientTable[clientId];
const entry = this.temporaryClientTable.get(clientId);
if (!entry) return undefined;
return entry;
}
async attachUserId(clientId: string, userId: string) {
if (!this.temporaryClientTable[clientId])
throw new Error("Invalid clientId for attaching userId");
this.temporaryClientTable[clientId].userId = userId;
const clientTable = this.temporaryClientTable.get(clientId);
if (!clientTable) throw new Error("Invalid clientId for attaching userId");
clientTable.userId = userId;
}
async generateAuthToken(clientId: string) {
const entry = this.temporaryClientTable[clientId];
const entry = this.temporaryClientTable.get(clientId);
if (!entry) throw new Error("Invalid clientId to generate token");
const token = randomUUID();
this.temporaryClientTable[clientId].authToken = token;
entry.authToken = token;
return token;
}
@@ -66,7 +69,7 @@ export class ClientHandler {
}
async finialiseClient(id: string) {
const metadata = this.temporaryClientTable[id];
const metadata = this.temporaryClientTable.get(id);
if (!metadata) throw new Error("Invalid client ID");
if (!metadata.userId) throw new Error("Un-authorized client ID");

View File

@@ -5,6 +5,5 @@ When a client signs on and registers itself as a peer
*/
class DownloadCoordinator {
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class, @typescript-eslint/no-unused-vars
class DownloadCoordinator {}

View File

@@ -33,7 +33,7 @@ class ManifestGenerator {
key,
Object.assign({}, value, { versionName: rootManifest.versionName }),
];
})
}),
);
}
@@ -71,6 +71,7 @@ class ManifestGenerator {
if (baseVersion.delta) {
// Start at the same index minus one, and keep grabbing them
// until we run out or we hit something that isn't a delta
// eslint-disable-next-line no-constant-condition
for (let i = baseVersion.versionIndex - 1; true; i--) {
const currentVersion = await prisma.gameVersion.findFirst({
where: {
@@ -88,7 +89,7 @@ class ManifestGenerator {
const metadata: DropManifestMetadata[] = leastToMost.map((e) => {
return {
manifest: JSON.parse(
e.dropletManifest?.toString() ?? "{}"
e.dropletManifest?.toString() ?? "{}",
) as DropManifest,
versionName: e.versionName,
};
@@ -96,7 +97,7 @@ class ManifestGenerator {
const manifest = ManifestGenerator.generateManifestFromMetadata(
metadata[0],
...metadata.slice(1)
...metadata.slice(1),
);
return manifest;

View File

@@ -8,8 +8,7 @@
import fs from "fs";
import path from "path";
import prisma from "../db/database";
import type { GameVersion} from "@prisma/client";
import { Platform } from "@prisma/client";
import type { GameVersion } from "@prisma/client";
import { fuzzy } from "fast-fuzzy";
import { recursivelyReaddir } from "../utils/recursivedirs";
import taskHandler from "../tasks";
@@ -52,13 +51,13 @@ class LibraryManager {
async fetchUnimportedGameVersions(
libraryBasePath: string,
versions: Array<GameVersion>
versions: Array<GameVersion>,
) {
const gameDir = path.join(this.basePath, libraryBasePath);
const versionsDirs = fs.readdirSync(gameDir);
const importedVersionDirs = versions.map((e) => e.versionName);
const unimportedVersions = versionsDirs.filter(
(e) => !importedVersionDirs.includes(e)
(e) => !importedVersionDirs.includes(e),
);
return unimportedVersions;
@@ -89,10 +88,10 @@ class LibraryManager {
noVersions: e.versions.length == 0,
unimportedVersions: await this.fetchUnimportedGameVersions(
e.libraryBasePath,
e.versions
e.versions,
),
},
}))
})),
);
}
@@ -113,7 +112,7 @@ class LibraryManager {
const targetDir = path.join(this.basePath, game.libraryBasePath);
if (!fs.existsSync(targetDir))
throw new Error(
"Game in database, but no physical directory? Something is very very wrong..."
"Game in database, but no physical directory? Something is very very wrong...",
);
const versions = fs.readdirSync(targetDir);
const validVersions = versions.filter((versionDir) => {
@@ -124,7 +123,7 @@ class LibraryManager {
const currentVersions = game.versions.map((e) => e.versionName);
const unimportedVersions = validVersions.filter(
(e) => !currentVersions.includes(e)
(e) => !currentVersions.includes(e),
);
return unimportedVersions;
}
@@ -138,7 +137,7 @@ class LibraryManager {
const targetDir = path.join(
this.basePath,
game.libraryBasePath,
versionName
versionName,
);
if (!fs.existsSync(targetDir)) return undefined;
@@ -217,7 +216,7 @@ class LibraryManager {
delta: boolean;
umuId: string;
}
},
) {
const taskId = `import:${gameId}:${versionName}`;
@@ -254,7 +253,7 @@ class LibraryManager {
(err, manifest) => {
if (err) return reject(err);
resolve(manifest);
}
},
);
});

View File

@@ -1,10 +1,5 @@
import type {
Developer,
Publisher} from "@prisma/client";
import {
MetadataSource,
PrismaClient
} from "@prisma/client";
import type { Developer, Publisher } from "@prisma/client";
import { MetadataSource } from "@prisma/client";
import prisma from "../db/database";
import type {
_FetchDeveloperMetadataParams,
@@ -17,11 +12,7 @@ import type {
PublisherMetadata,
} from "./types";
import { ObjectTransactionalHandler } from "../objects/transactional";
import { PriorityList, PriorityListIndexed } from "../utils/prioritylist";
import { GiantBombProvider } from "./giantbomb";
import { ManualMetadataProvider } from "./manual";
import { PCGamingWikiProvider } from "./pcgamingwiki";
import { IGDBProvider } from "./igdb";
import { PriorityListIndexed } from "../utils/prioritylist";
export class MissingMetadataProviderConfig extends Error {
private providerName: string;
@@ -47,10 +38,10 @@ export abstract class MetadataProvider {
abstract search(query: string): Promise<GameMetadataSearchResult[]>;
abstract fetchGame(params: _FetchGameMetadataParams): Promise<GameMetadata>;
abstract fetchPublisher(
params: _FetchPublisherMetadataParams
params: _FetchPublisherMetadataParams,
): Promise<PublisherMetadata>;
abstract fetchDeveloper(
params: _FetchDeveloperMetadataParams
params: _FetchDeveloperMetadataParams,
): Promise<DeveloperMetadata>;
}
@@ -81,6 +72,8 @@ export class MetadataHandler {
for (const provider of this.providers.values()) {
const queryTransformationPromise = new Promise<
InternalGameMetadataResult[]
// TODO: fix eslint error
// eslint-disable-next-line no-async-promise-executor
>(async (resolve, reject) => {
try {
const results = await provider.search(query);
@@ -89,7 +82,7 @@ export class MetadataHandler {
Object.assign({}, result, {
sourceId: provider.id(),
sourceName: provider.name(),
})
}),
);
resolve(mappedResults);
} catch (e) {
@@ -119,13 +112,13 @@ export class MetadataHandler {
sourceId: "manual",
sourceName: "Manual",
},
libraryBasePath
libraryBasePath,
);
}
async createGame(
result: InternalGameMetadataResult,
libraryBasePath: string
libraryBasePath: string,
) {
const provider = this.providers.get(result.sourceId);
if (!provider)
@@ -143,7 +136,7 @@ export class MetadataHandler {
const [createObject, pullObjects, dumpObjects] = this.objectHandler.new(
{},
["internal:read"]
["internal:read"],
);
let metadata;
@@ -197,7 +190,7 @@ export class MetadataHandler {
return (await this.fetchDeveloperPublisher(
query,
"fetchDeveloper",
"developer"
"developer",
)) as Developer;
}
@@ -205,7 +198,7 @@ export class MetadataHandler {
return (await this.fetchDeveloperPublisher(
query,
"fetchPublisher",
"publisher"
"publisher",
)) as Publisher;
}
@@ -214,8 +207,9 @@ export class MetadataHandler {
private async fetchDeveloperPublisher(
query: string,
functionName: "fetchDeveloper" | "fetchPublisher",
databaseName: "developer" | "publisher"
databaseName: "developer" | "publisher",
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const existing = await (prisma as any)[databaseName].findFirst({
where: {
metadataOriginalQuery: query,
@@ -229,7 +223,7 @@ export class MetadataHandler {
const [createObject, pullObjects, dumpObjects] = this.objectHandler.new(
{},
["internal:read"]
["internal:read"],
);
let result: PublisherMetadata;
try {
@@ -243,6 +237,7 @@ export class MetadataHandler {
// If we're successful
await pullObjects();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const object = await (prisma as any)[databaseName].create({
data: {
metadataSource: provider.source(),
@@ -262,7 +257,7 @@ export class MetadataHandler {
}
throw new Error(
`No metadata provider found a ${databaseName} for "${query}"`
`No metadata provider found a ${databaseName} for "${query}"`,
);
}
}

View File

@@ -6,9 +6,7 @@ import type {
_FetchPublisherMetadataParams,
PublisherMetadata,
_FetchDeveloperMetadataParams,
DeveloperMetadata} from "./types";
import {
GameMetadataSearchResult
DeveloperMetadata,
} from "./types";
import * as jdenticon from "jdenticon";
@@ -22,13 +20,11 @@ export class ManualMetadataProvider implements MetadataProvider {
source() {
return MetadataSource.Manual;
}
async search(query: string) {
async search(_query: string) {
return [];
}
async fetchGame({
name,
publisher,
developer,
createObject,
}: _FetchGameMetadataParams): Promise<GameMetadata> {
const icon = jdenticon.toPng(name, 512);
@@ -52,12 +48,12 @@ export class ManualMetadataProvider implements MetadataProvider {
};
}
async fetchPublisher(
params: _FetchPublisherMetadataParams
_params: _FetchPublisherMetadataParams,
): Promise<PublisherMetadata> {
throw new Error("Method not implemented.");
}
async fetchDeveloper(
params: _FetchDeveloperMetadataParams
_params: _FetchDeveloperMetadataParams,
): Promise<DeveloperMetadata> {
throw new Error("Method not implemented.");
}

View File

@@ -1,7 +1,6 @@
import type { Developer, Publisher } from "@prisma/client";
import { MetadataSource } from "@prisma/client";
import type { MetadataProvider} from ".";
import { MissingMetadataProviderConfig } from ".";
import type { MetadataProvider } from ".";
import type {
GameMetadataSearchResult,
_FetchGameMetadataParams,
@@ -48,7 +47,7 @@ interface PCGamingWikiCargoResult<T> {
cargoquery: [
{
title: T;
}
},
];
error?: {
code?: string;
@@ -61,8 +60,6 @@ interface PCGamingWikiCargoResult<T> {
// Api Docs: https://www.pcgamingwiki.com/wiki/PCGamingWiki:API
// Good tool for helping build cargo queries: https://www.pcgamingwiki.com/wiki/Special:CargoQuery
export class PCGamingWikiProvider implements MetadataProvider {
constructor() {}
id() {
return "pcgamingwiki";
}
@@ -75,7 +72,7 @@ export class PCGamingWikiProvider implements MetadataProvider {
private async request<T>(
query: URLSearchParams,
options?: AxiosRequestConfig
options?: AxiosRequestConfig,
) {
const finalURL = `https://www.pcgamingwiki.com/w/api.php?${query.toString()}`;
@@ -84,12 +81,12 @@ export class PCGamingWikiProvider implements MetadataProvider {
baseURL: "",
};
const response = await axios.request<PCGamingWikiCargoResult<T>>(
Object.assign({}, options, overlay)
Object.assign({}, options, overlay),
);
if (response.status !== 200)
throw new Error(
`Error in pcgamingwiki \nStatus Code: ${response.status}`
`Error in pcgamingwiki \nStatus Code: ${response.status}`,
);
else if (response.data.error !== undefined)
throw new Error(`Error in pcgamingwiki, malformed query`);
@@ -256,7 +253,7 @@ export class PCGamingWikiProvider implements MetadataProvider {
}
async fetchDeveloper(
params: _FetchDeveloperMetadataParams
params: _FetchDeveloperMetadataParams,
): Promise<DeveloperMetadata> {
return await this.fetchPublisher(params);
}

View File

@@ -1,6 +1,5 @@
import type { Developer, Publisher } from "@prisma/client";
import type { TransactionDataType } from "../objects/transactional";
import { ObjectTransactionalHandler } from "../objects/transactional";
import type { ObjectReference } from "../objects/objectHandler";
export interface GameMetadataSearchResult {

View File

@@ -1,4 +1,3 @@
import { triggerAsyncId } from "async_hooks";
import prisma from "../db/database";
import objectHandler from "../objects";
@@ -50,7 +49,7 @@ class NewsManager {
orderBy?: "asc" | "desc";
tags?: string[];
search?: string;
} = {}
} = {},
) {
return await prisma.article.findMany({
where: {
@@ -116,7 +115,7 @@ class NewsManager {
content?: string;
excerpt?: string;
image?: string;
}
},
) {
return await prisma.article.update({
where: { id },

View File

@@ -15,27 +15,28 @@ export type NotificationCreateArgs = Pick<
>;
class NotificationSystem {
private listeners: {
[key: string]: Map<string, (notification: Notification) => any>;
} = {};
private listeners = new Map<
string,
Map<string, (notification: Notification) => void>
>();
listen(
userId: string,
id: string,
callback: (notification: Notification) => any
callback: (notification: Notification) => void,
) {
this.listeners[userId] ??= new Map();
this.listeners[userId].set(id, callback);
this.listeners.set(userId, new Map());
this.listeners.get(userId)?.set(id, callback);
this.catchupListener(userId, id);
}
unlisten(userId: string, id: string) {
this.listeners[userId].delete(id);
this.listeners.get(userId)?.delete(id);
}
private async catchupListener(userId: string, id: string) {
const callback = this.listeners[userId].get(id);
const callback = this.listeners.get(userId)?.get(id);
if (!callback)
throw new Error("Failed to catch-up listener: callback does not exist");
const notifications = await prisma.notification.findMany({
@@ -50,7 +51,7 @@ class NotificationSystem {
}
private async pushNotification(userId: string, notification: Notification) {
for (const listener of this.listeners[userId] ?? []) {
for (const listener of this.listeners.get(userId) ?? []) {
await listener[1](notification);
}
}

View File

@@ -4,7 +4,7 @@ import { ObjectBackend } from "./objectHandler";
import { LRUCache } from "lru-cache";
import fs from "fs";
import path from "path";
import { Readable, Stream } from "stream";
import { Readable } from "stream";
import { createHash } from "crypto";
import prisma from "../db/database";
@@ -40,7 +40,7 @@ export class FsObjectBackend extends ObjectBackend {
if (source instanceof Readable) {
const outputStream = fs.createWriteStream(objectPath);
source.pipe(outputStream, { end: true });
await new Promise((r, j) => source.on("end", r));
await new Promise((r, _j) => source.on("end", r));
return true;
}
@@ -61,7 +61,7 @@ export class FsObjectBackend extends ObjectBackend {
async create(
id: string,
source: Source,
metadata: ObjectMetadata
metadata: ObjectMetadata,
): Promise<ObjectReference | undefined> {
const objectPath = path.join(this.baseObjectPath, id);
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
@@ -104,7 +104,7 @@ export class FsObjectBackend extends ObjectBackend {
return true;
}
async fetchMetadata(
id: ObjectReference
id: ObjectReference,
): Promise<ObjectMetadata | undefined> {
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
if (!fs.existsSync(metadataPath)) return undefined;
@@ -113,7 +113,7 @@ export class FsObjectBackend extends ObjectBackend {
}
async writeMetadata(
id: ObjectReference,
metadata: ObjectMetadata
metadata: ObjectMetadata,
): Promise<boolean> {
const metadataPath = path.join(this.baseMetadataPath, `${id}.json`);
if (!fs.existsSync(metadataPath)) return false;
@@ -153,8 +153,6 @@ class FsHashStore {
max: 1000, // number of items
});
constructor() {}
/**
* Gets hash of object
* @param id
@@ -211,6 +209,8 @@ class FsHashStore {
id,
},
});
} catch {}
} catch {
/* empty */
}
}
}

View File

@@ -16,7 +16,7 @@
import { parse as getMimeTypeBuffer } from "file-type-mime";
import type { Writable } from "stream";
import Stream, { Readable } from "stream";
import { Readable } from "stream";
import { getMimeType as getMimeTypeStream } from "stream-mime-type";
export type ObjectReference = string;
@@ -50,19 +50,19 @@ 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>;
}
@@ -96,7 +96,7 @@ export class ObjectHandler {
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)
@@ -112,7 +112,7 @@ export class ObjectHandler {
async createWithStream(
id: string,
metadata: { [key: string]: string },
permissions: Array<string>
permissions: Array<string>,
) {
return this.backend.createWithWriteStream(id, {
permissions,
@@ -194,7 +194,7 @@ export class ObjectHandler {
async writeWithPermissions(
id: ObjectReference,
sourceFetcher: () => Promise<Source>,
userId?: string
userId?: string,
) {
const metadata = await this.backend.fetchMetadata(id);
if (!metadata) return false;

View File

@@ -1,6 +1,6 @@
import prisma from "../internal/db/database";
export default defineNitroPlugin(async () => {
export default defineNitroPlugin(async (_nitro) => {
// Ensure system user exists
// The system user owns any user-based code
// that we want to re-use for the app

View File

@@ -1,6 +1,6 @@
import prisma from "../internal/db/database";
export default defineNitroPlugin(async () => {
export default defineNitroPlugin(async (_nitro) => {
const userCount = await prisma.user.count({
where: { id: { not: "system" } },
});

View File

@@ -6,7 +6,7 @@ import { IGDBProvider } from "../internal/metadata/igdb";
import { ManualMetadataProvider } from "../internal/metadata/manual";
import { PCGamingWikiProvider } from "../internal/metadata/pcgamingwiki";
export default defineNitroPlugin(async () => {
export default defineNitroPlugin(async (_nitro) => {
const metadataProviders = [
GiantBombProvider,
PCGamingWikiProvider,