UNPKG

alepha

Version:

Alepha is a convention-driven TypeScript framework for building robust, end-to-end type-safe applications, from serverless APIs to full-stack React apps.

439 lines (438 loc) 20.7 kB
import { BucketDescriptor } from "alepha/bucket"; import * as _alepha_core1 from "alepha"; import { Alepha, FileLike, Static } from "alepha"; import "alepha/server/security"; import * as _alepha_postgres16 from "alepha/postgres"; import { Page } from "alepha/postgres"; import * as _alepha_server0 from "alepha/server"; import { Ok } from "alepha/server"; import { DateTime, DateTimeProvider, DurationLike } from "alepha/datetime"; import * as _alepha_logger0 from "alepha/logger"; import { UserAccountToken } from "alepha/security"; import * as typebox25 from "typebox"; import * as dayjs2 from "dayjs"; //#region src/entities/files.d.ts declare const files: _alepha_postgres16.EntityDescriptor<typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>>; type FileEntity = Static<typeof files.schema>; //#endregion //#region src/schemas/fileQuerySchema.d.ts declare const fileQuerySchema: typebox25.TObject<{ page: typebox25.TOptional<typebox25.TInteger>; size: typebox25.TOptional<typebox25.TInteger>; sort: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TOptional<typebox25.TString>; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; name: typebox25.TOptional<typebox25.TString>; mimeType: typebox25.TOptional<typebox25.TString>; creator: typebox25.TOptional<typebox25.TString>; createdAfter: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; createdBefore: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; }>; type FileQuery = Static<typeof fileQuerySchema>; //#endregion //#region src/schemas/fileResourceSchema.d.ts declare const fileResourceSchema: typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>; type FileResource = Static<typeof fileResourceSchema>; //#endregion //#region src/schemas/storageStatsSchema.d.ts declare const bucketStatsSchema: typebox25.TObject<{ bucket: typebox25.TString; totalSize: typebox25.TNumber; fileCount: typebox25.TNumber; }>; declare const mimeTypeStatsSchema: typebox25.TObject<{ mimeType: typebox25.TString; fileCount: typebox25.TNumber; }>; declare const storageStatsSchema: typebox25.TObject<{ totalSize: typebox25.TNumber; totalFiles: typebox25.TNumber; byBucket: typebox25.TArray<typebox25.TObject<{ bucket: typebox25.TString; totalSize: typebox25.TNumber; fileCount: typebox25.TNumber; }>>; byMimeType: typebox25.TArray<typebox25.TObject<{ mimeType: typebox25.TString; fileCount: typebox25.TNumber; }>>; }>; type BucketStats = Static<typeof bucketStatsSchema>; type MimeTypeStats = Static<typeof mimeTypeStatsSchema>; type StorageStats = Static<typeof storageStatsSchema>; //#endregion //#region src/services/FileService.d.ts declare class FileService { protected readonly alepha: Alepha; protected readonly log: _alepha_logger0.Logger; protected readonly fileRepository: _alepha_postgres16.RepositoryDescriptor<typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>>; protected readonly dateTimeProvider: DateTimeProvider; protected readonly defaultBucket: BucketDescriptor; protected onUploadFile: _alepha_core1.HookDescriptor<"bucket:file:uploaded">; protected onDeleteBucketFile: _alepha_core1.HookDescriptor<"bucket:file:deleted">; /** * Calculates SHA-256 checksum of a file. * * @param file - The file to calculate checksum for * @returns Hexadecimal string representation of the SHA-256 hash * @protected */ protected calculateChecksum(file: FileLike): Promise<string>; /** * Gets a bucket descriptor by name. * * @param bucketName - The name of the bucket to retrieve (defaults to "default") * @returns The bucket descriptor * @throws {NotFoundError} If the bucket is not found */ bucket(bucketName?: string): BucketDescriptor; /** * Finds files matching the given query criteria with pagination support. * Supports filtering by bucket, tags, name, mimeType, creator, and date range. * * @param q - Query parameters including bucket, tags, name, mimeType, creator, date range, pagination, and sorting * @returns Paginated list of file entities */ findFiles(q?: FileQuery): Promise<Page<FileEntity>>; /** * Finds files that have expired based on their expiration date. * Limited to 1000 files per call to prevent memory issues. * * @returns Array of expired file entities */ findExpiredFiles(): Promise<FileEntity[]>; /** * Calculates an expiration date based on a TTL (time to live) duration. * * @param ttl - Duration like "1 day", "2 hours", etc. * @returns DateTime representation of the expiration date, or undefined if no TTL provided * @protected */ protected getExpirationDate(ttl?: DurationLike): DateTime | undefined; /** * Uploads a file to a bucket and creates a database record with metadata. * Automatically calculates and stores the file checksum (SHA-256). * * @param file - The file to upload * @param options - Upload options including bucket, expiration, user, and tags * @param options.bucket - Target bucket name (defaults to "default") * @param options.expirationDate - When the file should expire * @param options.user - User performing the upload (for audit trail) * @param options.tags - Tags to associate with the file * @returns The created file entity with all metadata * @throws {NotFoundError} If the specified bucket doesn't exist */ uploadFile(file: FileLike, options?: { expirationDate?: string | DateTime; bucket?: string; user?: UserAccountToken; tags?: string[]; }): Promise<FileEntity>; /** * Streams a file from storage by its database ID. * * @param id - The database ID (UUID) of the file to stream * @returns The file object ready for streaming/downloading * @throws {NotFoundError} If the file doesn't exist in the database * @throws {FileNotFoundError} If the file exists in database but not in storage */ streamFile(id: string): Promise<FileLike>; /** * Updates file metadata (name, tags, expiration date). * Does not modify the actual file content in storage. * * @param id - The database ID (UUID) of the file to update * @param data - Partial file data to update * @param data.name - New file name * @param data.tags - New tags array * @param data.expirationDate - New expiration date * @returns The updated file entity * @throws {NotFoundError} If the file doesn't exist in the database */ updateFile(id: string, data: { name?: string; tags?: string[]; expirationDate?: DateTime; }): Promise<FileEntity>; /** * Deletes a file from both storage and database. * Handles cases where file is already deleted from storage gracefully. * Always ensures database record is removed even if storage deletion fails. * * @param id - The database ID (UUID) of the file to delete * @returns Success response with the deleted file ID * @throws {NotFoundError} If the file doesn't exist in the database */ deleteFile(id: string): Promise<Ok>; /** * Retrieves a file entity by its ID. * If already an entity object, returns it as-is (convenience method). * * @param id - Either a UUID string or an existing FileEntity object * @returns The file entity * @throws {NotFoundError} If the file doesn't exist in the database */ getFileById(id: string | FileEntity): Promise<FileEntity>; /** * Gets storage statistics including total size, file count, and breakdowns by bucket and MIME type. * * @returns Storage statistics with aggregated data */ getStorageStats(): Promise<StorageStats>; /** * Converts a file entity to a file resource (API response format). * Currently a pass-through, but allows for future transformation logic. * * @param entity - The file entity to convert * @returns The file resource for API responses */ entityToResource(entity: FileEntity): FileResource; } //#endregion //#region src/controllers/FileController.d.ts /** * REST API controller for file management operations. * Provides endpoints for uploading, downloading, listing, and deleting files. */ declare class FileController { protected readonly url = "/files"; protected readonly group = "files"; protected readonly fileService: FileService; /** * GET /files - Lists files with optional filtering and pagination. * Supports filtering by bucket and tags. */ readonly findFiles: _alepha_server0.ActionDescriptorFn<{ query: typebox25.TObject<{ page: typebox25.TOptional<typebox25.TInteger>; size: typebox25.TOptional<typebox25.TInteger>; sort: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TOptional<typebox25.TString>; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; name: typebox25.TOptional<typebox25.TString>; mimeType: typebox25.TOptional<typebox25.TString>; creator: typebox25.TOptional<typebox25.TString>; createdAfter: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; createdBefore: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; }>; response: _alepha_postgres16.TPage<typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>>; }>; /** * DELETE /files/:id - Deletes a file from both storage and database. * Removes the file from the bucket and cleans up the database record. */ readonly deleteFile: _alepha_server0.ActionDescriptorFn<{ params: typebox25.TObject<{ id: typebox25.TString; }>; response: typebox25.TObject<{ ok: typebox25.TBoolean; id: typebox25.TOptional<typebox25.TUnion<[typebox25.TString, typebox25.TInteger]>>; count: typebox25.TOptional<typebox25.TNumber>; }>; }>; /** * POST /files - Uploads a new file to storage. * Creates a database record with metadata and calculates checksum. * Optionally specify bucket and expiration date. */ readonly uploadFile: _alepha_server0.ActionDescriptorFn<{ body: typebox25.TObject<{ file: _alepha_core1.TFile; }>; query: typebox25.TObject<{ expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; bucket: typebox25.TOptional<typebox25.TString>; }>; response: typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>; }>; /** * PATCH /files/:id - Updates file metadata. * Allows updating name, tags, and expiration date without modifying file content. */ readonly updateFile: _alepha_server0.ActionDescriptorFn<{ params: typebox25.TObject<{ id: typebox25.TString; }>; body: typebox25.TObject<{ name: typebox25.TOptional<typebox25.TString>; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; }>; response: typebox25.TObject<{ id: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TString, typeof _alepha_postgres16.PG_PRIMARY_KEY>, typeof _alepha_postgres16.PG_DEFAULT>; version: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TInteger, typeof _alepha_postgres16.PG_VERSION>, typeof _alepha_postgres16.PG_DEFAULT>; createdAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_CREATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; updatedAt: _alepha_postgres16.PgAttr<_alepha_postgres16.PgAttr<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>, typeof _alepha_postgres16.PG_UPDATED_AT>, typeof _alepha_postgres16.PG_DEFAULT>; blobId: typebox25.TString; creator: typebox25.TOptional<typebox25.TString>; creatorRealm: typebox25.TOptional<typebox25.TString>; creatorName: typebox25.TOptional<typebox25.TString>; bucket: typebox25.TString; expirationDate: typebox25.TOptional<typebox25.TCodec<typebox25.TString, dayjs2.Dayjs>>; name: typebox25.TString; size: typebox25.TNumber; mimeType: typebox25.TString; tags: typebox25.TOptional<typebox25.TArray<typebox25.TString>>; checksum: typebox25.TOptional<typebox25.TString>; }>; }>; /** * GET /files/:id - Streams/downloads a file by its ID. * Returns the file content with appropriate Content-Type header. * Cached with ETag support for 1 year (immutable). */ readonly streamFile: _alepha_server0.ActionDescriptorFn<{ params: typebox25.TObject<{ id: typebox25.TString; }>; response: _alepha_core1.TFile; }>; } //#endregion //#region src/controllers/StorageStatsController.d.ts /** * REST API controller for storage analytics and statistics. * Provides endpoints for viewing storage usage metrics. */ declare class StorageStatsController { protected readonly url = "/files/stats"; protected readonly group = "files"; protected readonly fileService: FileService; /** * GET /files/stats - Gets storage statistics. * Returns aggregated data including total size, file count, * and breakdowns by bucket and MIME type. */ readonly getStats: _alepha_server0.ActionDescriptorFn<{ response: typebox25.TObject<{ totalSize: typebox25.TNumber; totalFiles: typebox25.TNumber; byBucket: typebox25.TArray<typebox25.TObject<{ bucket: typebox25.TString; totalSize: typebox25.TNumber; fileCount: typebox25.TNumber; }>>; byMimeType: typebox25.TArray<typebox25.TObject<{ mimeType: typebox25.TString; fileCount: typebox25.TNumber; }>>; }>; }>; } //#endregion //#region src/index.d.ts declare module "alepha/bucket" { interface BucketFileOptions { /** * Time to live for the files in the bucket. */ ttl?: DurationLike; /** * Tags for the bucket. */ tags?: string[]; /** * User performing the operation. */ user?: UserAccountToken; /** * Whether to persist the file metadata in the database. * * @default true */ persist?: boolean; } } /** * Provides file management API endpoints for Alepha applications. * * This module includes file upload, download, storage management, * and file metadata operations. * * @module alepha.api.files */ declare const AlephaApiFiles: _alepha_core1.Service<_alepha_core1.Module<{}>>; //#endregion export { AlephaApiFiles, BucketStats, FileController, FileEntity, FileService, MimeTypeStats, StorageStats, StorageStatsController, bucketStatsSchema, files, mimeTypeStatsSchema, storageStatsSchema }; //# sourceMappingURL=index.d.ts.map