mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
chore: Migrate to namespace
This commit is contained in:
@@ -1,119 +1,151 @@
|
||||
import { z } from "zod";
|
||||
import { fn } from "../utils";
|
||||
import { Database } from "../drizzle";
|
||||
import { Examples } from "../examples";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { createTransaction } from "../drizzle/transaction";
|
||||
import { ImageColor, ImageDimensions, imagesTable } from "./images.sql";
|
||||
|
||||
export namespace Images {
|
||||
const Image = z.object({
|
||||
hash: z.string().openapi({
|
||||
description: "A unique cryptographic hash identifier for the image, used for deduplication and URL generation",
|
||||
example: Examples.CommonImg[0].hash
|
||||
}),
|
||||
averageColor: ImageColor.openapi({
|
||||
description: "The calculated dominant color of the image with light/dark classification, used for UI theming",
|
||||
example: Examples.CommonImg[0].averageColor
|
||||
}),
|
||||
dimensions: ImageDimensions.openapi({
|
||||
description: "The width and height dimensions of the image in pixels",
|
||||
example: Examples.CommonImg[0].dimensions
|
||||
}),
|
||||
fileSize: z.number().int().openapi({
|
||||
description: "The size of the image file in bytes, used for storage and bandwidth calculations",
|
||||
example: Examples.CommonImg[0].fileSize
|
||||
})
|
||||
const Image = z.object({
|
||||
hash: z.string().openapi({
|
||||
description:
|
||||
"A unique cryptographic hash identifier for the image, used for deduplication and URL generation",
|
||||
example: Examples.CommonImg[0].hash,
|
||||
}),
|
||||
averageColor: ImageColor.openapi({
|
||||
description:
|
||||
"The calculated dominant color of the image with light/dark classification, used for UI theming",
|
||||
example: Examples.CommonImg[0].averageColor,
|
||||
}),
|
||||
dimensions: ImageDimensions.openapi({
|
||||
description: "The width and height dimensions of the image in pixels",
|
||||
example: Examples.CommonImg[0].dimensions,
|
||||
}),
|
||||
fileSize: z.number().int().openapi({
|
||||
description:
|
||||
"The size of the image file in bytes, used for storage and bandwidth calculations",
|
||||
example: Examples.CommonImg[0].fileSize,
|
||||
}),
|
||||
});
|
||||
|
||||
export const Info = z
|
||||
.object({
|
||||
screenshots: Image.array().openapi({
|
||||
description:
|
||||
"In-game captured images showing actual gameplay, user interface, and key moments",
|
||||
example: Examples.Images.screenshots,
|
||||
}),
|
||||
boxArts: Image.array().openapi({
|
||||
description:
|
||||
"Square 1:1 aspect ratio artwork, typically used for store listings and thumbnails",
|
||||
example: Examples.Images.boxArts,
|
||||
}),
|
||||
posters: Image.array().openapi({
|
||||
description:
|
||||
"Vertical 2:3 aspect ratio promotional artwork, similar to movie posters",
|
||||
example: Examples.Images.posters,
|
||||
}),
|
||||
banners: Image.array().openapi({
|
||||
description:
|
||||
"Horizontal promotional artwork optimized for header displays and banners",
|
||||
example: Examples.Images.banners,
|
||||
}),
|
||||
heroArts: Image.array().openapi({
|
||||
description:
|
||||
"High-resolution, wide-format artwork designed for featured content and main entries",
|
||||
example: Examples.Images.heroArts,
|
||||
}),
|
||||
backdrops: Image.array().openapi({
|
||||
description:
|
||||
"Full-width backdrop images optimized for page layouts and decorative purposes",
|
||||
example: Examples.Images.backdrops,
|
||||
}),
|
||||
logos: Image.array().openapi({
|
||||
description:
|
||||
"Official game logo artwork, typically with transparent backgrounds for flexible placement",
|
||||
example: Examples.Images.logos,
|
||||
}),
|
||||
icons: Image.array().openapi({
|
||||
description:
|
||||
"Small-format identifiers used for application shortcuts and compact displays",
|
||||
example: Examples.Images.icons,
|
||||
}),
|
||||
})
|
||||
.openapi({
|
||||
ref: "Images",
|
||||
description:
|
||||
"Complete collection of game-related visual assets, including promotional materials, UI elements, and store assets",
|
||||
example: Examples.Images,
|
||||
});
|
||||
|
||||
export const Info = z.object({
|
||||
screenshots: Image.array().openapi({
|
||||
description: "In-game captured images showing actual gameplay, user interface, and key moments",
|
||||
example: Examples.Images.screenshots
|
||||
}),
|
||||
boxArts: Image.array().openapi({
|
||||
description: "Square 1:1 aspect ratio artwork, typically used for store listings and thumbnails",
|
||||
example: Examples.Images.boxArts
|
||||
}),
|
||||
posters: Image.array().openapi({
|
||||
description: "Vertical 2:3 aspect ratio promotional artwork, similar to movie posters",
|
||||
example: Examples.Images.posters
|
||||
}),
|
||||
banners: Image.array().openapi({
|
||||
description: "Horizontal promotional artwork optimized for header displays and banners",
|
||||
example: Examples.Images.banners
|
||||
}),
|
||||
heroArts: Image.array().openapi({
|
||||
description: "High-resolution, wide-format artwork designed for featured content and main entries",
|
||||
example: Examples.Images.heroArts
|
||||
}),
|
||||
backdrops: Image.array().openapi({
|
||||
description: "Full-width backdrop images optimized for page layouts and decorative purposes",
|
||||
example: Examples.Images.backdrops
|
||||
}),
|
||||
logos: Image.array().openapi({
|
||||
description: "Official game logo artwork, typically with transparent backgrounds for flexible placement",
|
||||
example: Examples.Images.logos
|
||||
}),
|
||||
icons: Image.array().openapi({
|
||||
description: "Small-format identifiers used for application shortcuts and compact displays",
|
||||
example: Examples.Images.icons
|
||||
}),
|
||||
}).openapi({
|
||||
ref: "Images",
|
||||
description: "Complete collection of game-related visual assets, including promotional materials, UI elements, and store assets",
|
||||
example: Examples.Images
|
||||
})
|
||||
export type Info = z.infer<typeof Info>;
|
||||
|
||||
export type Info = z.infer<typeof Info>
|
||||
export const InputInfo = createSelectSchema(imagesTable).omit({
|
||||
timeCreated: true,
|
||||
timeDeleted: true,
|
||||
timeUpdated: true,
|
||||
});
|
||||
|
||||
export const InputInfo = createSelectSchema(imagesTable)
|
||||
.omit({ timeCreated: true, timeDeleted: true, timeUpdated: true })
|
||||
export const create = fn(InputInfo, (input) =>
|
||||
Database.transaction(async (tx) =>
|
||||
tx
|
||||
.insert(imagesTable)
|
||||
.values(input)
|
||||
.onConflictDoUpdate({
|
||||
target: [
|
||||
imagesTable.imageHash,
|
||||
imagesTable.type,
|
||||
imagesTable.baseGameID,
|
||||
imagesTable.position,
|
||||
],
|
||||
set: { timeDeleted: null },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export const create = fn(
|
||||
InputInfo,
|
||||
(input) =>
|
||||
createTransaction(async (tx) =>
|
||||
tx
|
||||
.insert(imagesTable)
|
||||
.values(input)
|
||||
.onConflictDoUpdate({
|
||||
target: [imagesTable.imageHash, imagesTable.type, imagesTable.baseGameID, imagesTable.position],
|
||||
set: { timeDeleted: null }
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
export function serialize(
|
||||
input: typeof imagesTable.$inferSelect[],
|
||||
): z.infer<typeof Info> {
|
||||
return input
|
||||
.sort((a, b) => {
|
||||
if (a.type === b.type) {
|
||||
return a.position - b.position;
|
||||
}
|
||||
return a.type.localeCompare(b.type);
|
||||
})
|
||||
.reduce<Record<`${typeof imagesTable.$inferSelect["type"]}s`, { hash: string; averageColor: ImageColor; dimensions: ImageDimensions; fileSize: number }[]>>((acc, img) => {
|
||||
const key = `${img.type}s` as `${typeof img.type}s`
|
||||
if (Array.isArray(acc[key])) {
|
||||
acc[key]!.push({
|
||||
hash: img.imageHash,
|
||||
averageColor: img.extractedColor,
|
||||
dimensions: img.dimensions,
|
||||
fileSize: img.fileSize
|
||||
})
|
||||
}
|
||||
return acc
|
||||
}, {
|
||||
screenshots: [],
|
||||
boxArts: [],
|
||||
banners: [],
|
||||
heroArts: [],
|
||||
posters: [],
|
||||
backdrops: [],
|
||||
icons: [],
|
||||
logos: [],
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
export function serialize(
|
||||
input: (typeof imagesTable.$inferSelect)[],
|
||||
): z.infer<typeof Info> {
|
||||
return input
|
||||
.sort((a, b) => {
|
||||
if (a.type === b.type) {
|
||||
return a.position - b.position;
|
||||
}
|
||||
return a.type.localeCompare(b.type);
|
||||
})
|
||||
.reduce<
|
||||
Record<
|
||||
`${(typeof imagesTable.$inferSelect)["type"]}s`,
|
||||
{
|
||||
hash: string;
|
||||
averageColor: ImageColor;
|
||||
dimensions: ImageDimensions;
|
||||
fileSize: number;
|
||||
}[]
|
||||
>
|
||||
>(
|
||||
(acc, img) => {
|
||||
const key = `${img.type}s` as `${typeof img.type}s`;
|
||||
if (Array.isArray(acc[key])) {
|
||||
acc[key]!.push({
|
||||
hash: img.imageHash,
|
||||
averageColor: img.extractedColor,
|
||||
dimensions: img.dimensions,
|
||||
fileSize: img.fileSize,
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
screenshots: [],
|
||||
boxArts: [],
|
||||
banners: [],
|
||||
heroArts: [],
|
||||
posters: [],
|
||||
backdrops: [],
|
||||
icons: [],
|
||||
logos: [],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user