feat: create domain port interfaces with JSDoc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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[]>;
|
||||
}
|
||||
@@ -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;
|
||||
}>;
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
Reference in New Issue
Block a user