feat: create domain port interfaces with JSDoc

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude
2026-07-28 17:48:24 +07:00
parent e7657453d5
commit ee3167fbfb
5 changed files with 318 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
import type { Bucket } from '../entities/bucket';
/**
* Repository interface for Bucket entity persistence.
*
* Abstracts the bucket CRUD operations currently in `src/db/buckets.ts`.
*/
export interface IBucketRepository {
/**
* Create a new bucket with the given name.
* @param name - The unique bucket name (S3 naming convention).
* @returns The newly created bucket record.
*/
create(name: string): Promise<Bucket>;
/**
* Find a bucket by its unique name.
* @param name - The bucket name to look up.
* @returns The matching bucket, or `null` when not found.
*/
findByName(name: string): Promise<Bucket | null>;
/**
* List all buckets, ordered alphabetically by name.
* @returns An array of all bucket records.
*/
list(): Promise<Bucket[]>;
/**
* Delete a bucket and cascade-delete all associated files and multipart data.
* @param name - The name of the bucket to delete.
* @returns `true` if the bucket was deleted, `false` if it did not exist.
*/
delete(name: string): Promise<boolean>;
/**
* Check whether a bucket with the given name exists.
* @param name - The bucket name to check.
* @returns `true` if the bucket exists, `false` otherwise.
*/
exists(name: string): Promise<boolean>;
}
+30
View File
@@ -0,0 +1,30 @@
import type { FilePart, NewFilePart } from '../entities/file-part';
/**
* Repository interface for FilePart entity persistence.
*
* Abstracts the file-part operations currently in `src/db/file-parts.ts`.
* File parts represent the chunks of a large file stored across multiple
* Telegram messages for Telegram-safe storage.
*/
export interface IFilePartRepository {
/**
* Insert multiple file parts in a single operation.
* @param parts - An array of new file part records (auto-generated fields omitted).
*/
insert(parts: NewFilePart[]): Promise<void>;
/**
* List all file parts for a given file, ordered by part number.
* @param fileId - The UUID of the parent file record.
* @returns An array of file parts.
*/
listByFileId(fileId: string): Promise<FilePart[]>;
/**
* Count the number of file parts associated with a file.
* @param fileId - The UUID of the parent file record.
* @returns The part count.
*/
countByFileId(fileId: string): Promise<number>;
}
+108
View File
@@ -0,0 +1,108 @@
import type { File, NewFile } from '../entities/file';
/**
* An S3-synced file record: a File entity guaranteed to have non-null
* bucketId and s3Key values.
*/
export interface S3FileRecord extends File {
/** S3 bucket UUID (non-null refinement) */
bucketId: string;
/** S3 object key (non-null refinement) */
s3Key: string;
}
/**
* Repository interface for File entity persistence.
*
* Abstracts all file CRUD operations currently spread across
* `src/db/files.ts` and `src/db/files-ext.ts`.
*/
export interface IFileRepository {
/**
* Find a single file by its SHA-256 content hash.
* @param hash - The SHA-256 hash to search for.
* @returns The matching file, or `null` when not found.
*/
findByHash(hash: string): Promise<File | null>;
/**
* Find a single file by its public-facing short identifier.
* @param publicId - The public ID to look up.
* @returns The matching file, or `null` when not found.
*/
findByPublicId(publicId: string): Promise<File | null>;
/**
* Find a single file by its Telegram file unique ID (stable across bot tokens).
* @param telegramFileUniqueId - The Telegram unique file ID.
* @returns The matching file, or `null` when not found.
*/
findByUniqueId(telegramFileUniqueId: string): Promise<File | null>;
/**
* Find a single file by its S3 bucket and object key.
* @param bucketId - The bucket UUID.
* @param s3Key - The S3 object key.
* @returns The matching file, or `null` when not found.
*/
findByBucketAndKey(bucketId: string, s3Key: string): Promise<File | null>;
/**
* Create a new file record.
* @param file - The file data (auto-generated fields omitted).
* @returns The newly created file record with all fields populated.
*/
create(file: NewFile): Promise<File>;
/**
* List objects within a bucket, optionally filtered by prefix and delimiter.
*
* When `delimiter` is `"/"`, common prefixes (pseudo-directories) are
* returned separately and objects whose key continues past the delimiter
* are omitted from the `objects` array.
*
* @param bucketId - The bucket UUID to list from.
* @param prefix - Key prefix to filter by.
* @param delimiter - Delimiter character (e.g. `"/"`) or `null` for flat listing.
* @param maxKeys - Maximum number of object records to return.
* @param startAfter - Return only keys strictly greater than this value, or `null`.
* @returns A list of matching S3 file records and discovered common prefixes.
*/
listByPrefix(
bucketId: string,
prefix: string,
delimiter: string | null,
maxKeys: number,
startAfter: string | null,
): Promise<{ objects: S3FileRecord[]; prefixes: string[] }>;
/**
* Soft-delete a single file by bucket and key.
* @param bucketId - The bucket UUID.
* @param s3Key - The S3 object key.
* @returns `true` if a row was soft-deleted, `false` otherwise.
*/
softDelete(bucketId: string, s3Key: string): Promise<boolean>;
/**
* Soft-delete multiple files within a bucket in batch.
* @param bucketId - The bucket UUID.
* @param keys - Array of S3 object keys to delete.
* @returns The number of rows actually soft-deleted.
*/
softDeleteBatch(bucketId: string, keys: string[]): Promise<number>;
/**
* Count non-deleted objects in a bucket.
* @param bucketId - The bucket UUID.
* @returns The object count.
*/
countByBucket(bucketId: string): Promise<number>;
/**
* Find soft-deleted (orphaned) file records in a bucket.
* @param bucketId - The bucket UUID.
* @returns An array of orphaned file records.
*/
findOrphansByBucket(bucketId: string): Promise<File[]>;
}
+70
View File
@@ -0,0 +1,70 @@
import type { MultipartUpload, MultipartPart } from '../entities/multipart';
/**
* Repository interface for S3 multipart upload persistence.
*
* Abstracts the multipart upload operations currently in `src/db/multipart.ts`.
* Manages both multipart upload sessions and their individual parts.
*/
export interface IMultipartRepository {
/**
* Initiate a new multipart upload session.
* @param bucketId - The UUID of the target bucket.
* @param s3Key - The S3 object key being uploaded.
* @param initiatedBy - Identifier of the entity that initiated the upload.
* @returns The newly generated upload ID (nanoid).
*/
create(bucketId: string, s3Key: string, initiatedBy: string): Promise<string>;
/**
* Find an in-progress multipart upload by its upload ID.
* @param uploadId - The upload identifier.
* @returns The matching upload, or `null` if not found or not in progress.
*/
findById(uploadId: string): Promise<MultipartUpload | null>;
/**
* Mark a multipart upload as completed.
* @param uploadId - The upload identifier to complete.
*/
complete(uploadId: string): Promise<void>;
/**
* Mark a multipart upload as aborted.
* @param uploadId - The upload identifier to abort.
*/
abort(uploadId: string): Promise<void>;
/**
* Insert a single part record for a multipart upload.
* @param part - The part data (auto-generated fields omitted).
*/
insertPart(part: Omit<MultipartPart, 'id' | 'createdAt'>): Promise<void>;
/**
* List all parts for a multipart upload, ordered by part number.
* @param uploadId - The upload identifier.
* @returns An array of multipart parts.
*/
listParts(uploadId: string): Promise<MultipartPart[]>;
/**
* List in-progress multipart uploads within a bucket, with pagination.
*
* Results are ordered by S3 key and initiation timestamp.
*
* @param bucketId - The UUID of the bucket.
* @param maxUploads - Maximum number of uploads to return (clamped 1-1000).
* @param keyMarker - Return only uploads whose S3 key is strictly greater than this, or `null`.
* @returns A list of uploads and pagination metadata.
*/
listByBucket(
bucketId: string,
maxUploads: number,
keyMarker: string | null,
): Promise<{
uploads: MultipartUpload[];
isTruncated: boolean;
nextKeyMarker: string | null;
}>;
}
+68
View File
@@ -0,0 +1,68 @@
/**
* Result of forwarding a file to Telegram storage.
*/
export interface ForwardResult {
/** The Telegram file_id for retrieving the file */
telegramFileId: string;
/** The Telegram unique file_id (stable across bot tokens) */
telegramFileUniqueId: string;
/** The message ID within the storage chat */
storageMessageId: number;
}
/**
* File information returned by Telegram's getFile API.
*/
export interface TelegramFileInfo {
/** File size in bytes */
file_size: number;
/** MIME type of the file */
mime_type: string;
/** Path on Telegram's file server for downloading */
file_path: string;
/** Bot token that owns the retrieved file */
bot_token: string;
}
/**
* Abstraction over Telegram bot API operations.
*
* Defines the contract for forwarding files to Telegram storage,
* retrieving file metadata, and managing concurrent uploads.
*/
export interface ITelegramService {
/**
* Forward a file chunk to the configured Telegram storage chat.
*
* @param fileChunk - The file data (ReadStream, Buffer, or file path).
* @param fileName - The original file name.
* @param fileType - The file type classification (e.g. "photo", "document").
* @returns The Telegram identifiers of the stored file.
*/
forwardToStorage(
fileChunk: unknown,
fileName: string,
fileType: string,
): Promise<ForwardResult>;
/**
* Retrieve file metadata from Telegram by file ID.
*
* Tries all configured bots; returns info from the first that owns the file.
*
* @param telegramFileId - The Telegram file_id to look up.
* @returns Metadata including size, MIME type, download path, and bot token.
*/
getFileInfo(telegramFileId: string): Promise<TelegramFileInfo>;
/**
* Enqueue a task for sequential upload execution.
*
* Ensures only one Telegram upload runs at a time to avoid
* rate limits and resource contention.
*
* @param task - An async function performing the upload.
* @returns The result of the task.
*/
enqueueUpload<T>(task: () => Promise<T>): Promise<T>;
}