sendou.ink/app/features/img-upload/ImageRepository.server.ts

179 lines
5.0 KiB
TypeScript

import { db } from "~/db/sql";
import { databaseTimestampNow } from "~/utils/dates";
import { concatUserSubmittedImagePrefix } from "~/utils/kysely.server";
import { IMAGES_TO_VALIDATE_AT_ONCE } from "./upload-constants";
import type { ImageUploadType } from "./upload-types";
/** Finds an unvalidated image by ID with associated calendar event data */
export function findById(id: number) {
return db
.selectFrom("UnvalidatedUserSubmittedImage")
.leftJoin(
"CalendarEvent",
"CalendarEvent.avatarImgId",
"UnvalidatedUserSubmittedImage.id",
)
.select(["CalendarEvent.tournamentId"])
.where("UnvalidatedUserSubmittedImage.id", "=", id)
.executeTakeFirst();
}
/** Deletes an image and its associated art entry in a transaction */
export function deleteImageById(id: number) {
return db.transaction().execute(async (trx) => {
await trx.deleteFrom("Art").where("Art.imgId", "=", id).execute();
await trx
.deleteFrom("UnvalidatedUserSubmittedImage")
.where("id", "=", id)
.execute();
});
}
/** Counts unvalidated art images for a specific author */
export async function countUnvalidatedArt(authorId: number) {
const result = await db
.selectFrom("UnvalidatedUserSubmittedImage")
.innerJoin("Art", "Art.imgId", "UnvalidatedUserSubmittedImage.id")
.select(({ fn }) => fn.countAll<number>().as("count"))
.where("UnvalidatedUserSubmittedImage.validatedAt", "is", null)
.where("Art.authorId", "=", authorId)
.executeTakeFirstOrThrow();
return result.count;
}
const unvalidatedImagesBaseQuery = db
.selectFrom("UnvalidatedUserSubmittedImage")
.leftJoin("Team", (join) =>
join.on((eb) =>
eb.or([
eb("UnvalidatedUserSubmittedImage.id", "=", eb.ref("Team.avatarImgId")),
eb("UnvalidatedUserSubmittedImage.id", "=", eb.ref("Team.bannerImgId")),
]),
),
)
.leftJoin("Art", "UnvalidatedUserSubmittedImage.id", "Art.imgId")
.leftJoin(
"CalendarEvent",
"UnvalidatedUserSubmittedImage.id",
"CalendarEvent.avatarImgId",
)
.where("UnvalidatedUserSubmittedImage.validatedAt", "is", null)
.where((eb) =>
eb.or([
eb("Team.id", "is not", null),
eb("Art.id", "is not", null),
eb("CalendarEvent.id", "is not", null),
]),
);
/** Counts all unvalidated images used in teams, art, or calendar events */
export async function countAllUnvalidated() {
const result = await unvalidatedImagesBaseQuery
.select(({ fn }) => fn.countAll<number>().as("count"))
.executeTakeFirstOrThrow();
return result.count;
}
/** Fetches unvalidated images for admin review with submitter info */
export function unvalidatedImages() {
return unvalidatedImagesBaseQuery
.leftJoin(
"User",
"UnvalidatedUserSubmittedImage.submitterUserId",
"User.id",
)
.select((eb) => [
"UnvalidatedUserSubmittedImage.id",
concatUserSubmittedImagePrefix(
eb.ref("UnvalidatedUserSubmittedImage.url"),
).as("url"),
"UnvalidatedUserSubmittedImage.submitterUserId",
"User.username",
])
.limit(IMAGES_TO_VALIDATE_AT_ONCE)
.execute();
}
/** Counts unvalidated team images submitted by a specific user */
export async function countUnvalidatedBySubmitterUserId(userId: number) {
const result = await db
.selectFrom("UnvalidatedUserSubmittedImage")
.innerJoin("Team", (join) =>
join.on((eb) =>
eb.or([
eb(
"UnvalidatedUserSubmittedImage.id",
"=",
eb.ref("Team.avatarImgId"),
),
eb(
"UnvalidatedUserSubmittedImage.id",
"=",
eb.ref("Team.bannerImgId"),
),
]),
),
)
.select(({ fn }) => fn.countAll<number>().as("count"))
.where("UnvalidatedUserSubmittedImage.validatedAt", "is", null)
.where("UnvalidatedUserSubmittedImage.submitterUserId", "=", userId)
.executeTakeFirstOrThrow();
return result.count;
}
/** Marks an image as validated by setting the current timestamp */
export function validateImage(id: number) {
return db
.updateTable("UnvalidatedUserSubmittedImage")
.set({ validatedAt: databaseTimestampNow() })
.where("id", "=", id)
.execute();
}
/** Creates a new image and associates it with a team or organization */
export function addNewImage({
submitterUserId,
url,
validatedAt,
teamId,
organizationId,
type,
}: {
submitterUserId: number;
url: string;
validatedAt: number | null;
teamId?: number;
organizationId?: number;
type: ImageUploadType;
}) {
return db.transaction().execute(async (trx) => {
const img = await trx
.insertInto("UnvalidatedUserSubmittedImage")
.values({ submitterUserId, url, validatedAt })
.returningAll()
.executeTakeFirstOrThrow();
if (type === "team-pfp" && teamId) {
await trx
.updateTable("AllTeam")
.set({ avatarImgId: img.id })
.where("id", "=", teamId)
.execute();
} else if (type === "team-banner" && teamId) {
await trx
.updateTable("AllTeam")
.set({ bannerImgId: img.id })
.where("id", "=", teamId)
.execute();
} else if (type === "org-pfp" && organizationId) {
await trx
.updateTable("TournamentOrganization")
.set({ avatarImgId: img.id })
.where("id", "=", organizationId)
.execute();
}
return img;
});
}