feat: add initial storage module
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
export const account = {
|
||||
create: (await import("./routes/create/spec.ts")).default,
|
||||
get: (await import("./routes/get/spec.ts")).default,
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "@module/account",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./server": "./server.ts",
|
||||
"./client": "./client.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@platform/relay": "workspace:*",
|
||||
"zod": "4.3.5"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import route from "./spec.ts";
|
||||
|
||||
export default route.handle(async ({ body }) => {
|
||||
console.log(body);
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import { route } from "@platform/relay";
|
||||
import z from "zod";
|
||||
|
||||
export default route.post("/api/v1/account").body(
|
||||
z.strictObject({
|
||||
tenantId: z.uuid().describe("Tenant identifier the account belongs to"),
|
||||
userId: z.uuid().describe("User identifier the account belongs to"),
|
||||
account: z.strictObject({
|
||||
type: z.string().describe("Type of account being created"),
|
||||
number: z.number().describe("Unique account identifier to create for the account"),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
@@ -1,5 +0,0 @@
|
||||
import route from "./spec.ts";
|
||||
|
||||
export default route.handle(async ({ params }) => {
|
||||
console.log(params);
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
import { route } from "@platform/relay";
|
||||
import z from "zod";
|
||||
|
||||
export default route.get("/api/v1/account/:number").params({
|
||||
number: z.number().describe("Account number to retrieve"),
|
||||
});
|
||||
1
modules/storage/entrypoints/client.ts
Normal file
1
modules/storage/entrypoints/client.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "../schemas/image.ts";
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@module/tenant",
|
||||
"name": "@module/storage",
|
||||
"description": "Handles storage, consumption and management of files.",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
121
modules/storage/schemas/image.ts
Normal file
121
modules/storage/schemas/image.ts
Normal 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;
|
||||
@@ -1,3 +0,0 @@
|
||||
export const tenant = {
|
||||
create: (await import("../routes/create/spec.ts")).default,
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
export default {
|
||||
routes: [(await import("../routes/create/handle.ts")).default],
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
| Tenant
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export const TenantSchema = z.strictObject({
|
||||
_id: z.uuid().describe("Primary identifier of the account"),
|
||||
name: z.string().describe("Human-readable name for the tenant"),
|
||||
});
|
||||
|
||||
export type Tenant = z.output<typeof TenantSchema>;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
| Database
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export const TenantInsertSchema = z.strictObject({
|
||||
name: TenantSchema.shape.name,
|
||||
});
|
||||
|
||||
export type TenantInsert = z.input<typeof TenantInsertSchema>;
|
||||
Reference in New Issue
Block a user