feat: add initial storage module

This commit is contained in:
2026-01-09 01:42:57 +01:00
parent 7336cfd411
commit 304c7969b2
60 changed files with 5988 additions and 107 deletions

View File

@@ -0,0 +1 @@
export * from "../schemas/image.ts";

View File

View File

@@ -0,0 +1,17 @@
{
"name": "@module/storage",
"description": "Handles storage, consumption and management of files.",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
"./server": "./entrypoints/server.ts",
"./client": "./entrypoints/client.ts"
},
"dependencies": {
"@platform/database": "workspace:*",
"@platform/parse": "workspace:*",
"@platform/relay": "workspace:*",
"zod": "4.3.5"
}
}

View File

@@ -0,0 +1,121 @@
import z from "zod";
/**
* Supported image formats as string literals.
*/
const IMAGE_FORMATS = ["png", "jpeg", "gif", "webp", "bmp"] as const;
/**
* Map of supported image format keys to their MIME types.
*/
const MIME_MAP = {
png: "image/png",
jpeg: "image/jpeg",
gif: "image/gif",
webp: "image/webp",
bmp: "image/bmp",
} as const;
/*
|--------------------------------------------------------------------------------
| Raw Image
|--------------------------------------------------------------------------------
|
| Defines the raw structure of an image as stored in the database.
|
| This schema captures all the immutable attributes of an image (id, folder
| reference, URL, file metadata, etc.) but does not include any derived values
| such as the MIME type.
|
*/
const RawImageSchema = z.strictObject({
_id: z.uuid().describe("Primary identifier of the image"),
folderId: z.uuid().optional().describe("Primary identifier of the folder the image belongs to"),
name: z.string().min(1).describe("Original file name (e.g., cat.png)"),
format: z.enum(IMAGE_FORMATS).describe("Image format"),
size: z.number().positive().max(500).describe("File size in bytes"),
hash: z.string().min(1).describe("Image hash to ensure we don't store the same image multiple times"),
width: z.number().positive().describe("Image width in pixels"),
height: z.number().positive().describe("Image height in pixels"),
url: z.url().describe("Public URL to access the image"),
description: z.string().max(500).optional().describe("Optional description or caption (max 500 chars)"),
tags: z.array(z.string().min(1)).default([]).describe("Array of tags for search / categorization"),
createdAt: z.coerce.date().describe("Timestamp when the image was stored"),
});
/*
|--------------------------------------------------------------------------------
| Image
|--------------------------------------------------------------------------------
|
| A strict representation of an image that includes a derived `mime` field. The
| `ImageSchema` takes `RawImageSchema` and attaches the MIME type based on the
| format. No extra keys are permitted.
|
*/
export const ImageSchema = RawImageSchema.transform((data) => ({
...data,
mime: mimeForFormat(data.format),
}));
export type Image = z.output<typeof ImageSchema>;
/*
|--------------------------------------------------------------------------------
| Database
|--------------------------------------------------------------------------------
|
| Schemas used for interacting with the persistence layer.
|
| `ImageInsertSchema` is used when inserting a new image it omits
| automatically generated fields such as `_id` and `createdAt`. It also
| normalises the `tags` array to contain unique values.
|
*/
/**
* Schema used when inserting a new image.
* It omits _id, and createdAt those are generated by the DB.
*/
export const ImageInsertSchema = z
.strictObject({
...RawImageSchema.omit({ _id: true, createdAt: true }).shape,
})
.transform((data) => ({
...data,
tags: Array.from(new Set(data.tags)),
}));
export type ImageInsert = z.input<typeof ImageInsertSchema>;
/*
|--------------------------------------------------------------------------------
| Utilities
|--------------------------------------------------------------------------------
*/
/**
* Returns the MIME type corresponding to the given image format.
* If the format is unknown, defaults to "application/octet-stream".
*
* @param format - The image format key (e.g., "png").
*
* @returns The MIME type string.
*/
function mimeForFormat<TKey extends keyof MIMEMap>(format: TKey): MIMEMap[TKey] | "application/octet-stream" {
return MIME_MAP[format] ?? "application/octet-stream";
}
/*
|--------------------------------------------------------------------------------
| Types
|--------------------------------------------------------------------------------
*/
type MIMEMap = typeof MIME_MAP;