UNPKG

@firesystem/s3

Version:
161 lines (142 loc) 5.19 kB
import type { IReactiveFileSystem, IFileSystemCapabilities, } from "@firesystem/core"; // Import type-only para evitar dependência em runtime import type { SourceProvider } from "@workspace-fs/core"; import { S3FileSystem } from "../S3FileSystem"; import type { S3FileSystemConfig } from "../types"; export class S3WorkspaceProvider implements SourceProvider { readonly scheme = "s3"; readonly displayName = "AWS S3 Storage"; async createFileSystem(config: any): Promise<IReactiveFileSystem> { // Validar configuração primeiro const validation = await this.validateConfiguration(config); if (!validation.valid) { throw new Error( `Invalid S3 configuration: ${validation.errors?.join(", ")}`, ); } // Configuração padrão se não fornecida const fsConfig: S3FileSystemConfig = { bucket: config.bucket, region: config.region, prefix: config.prefix || "/", mode: config.mode || "strict", credentials: config.credentials, clientOptions: config.clientOptions, }; const fs = new S3FileSystem(fsConfig); // S3FileSystem tem método initialize se precisar conectar if ("initialize" in fs && typeof fs.initialize === "function") { await fs.initialize(); } return fs; } getCapabilities(): IFileSystemCapabilities { return { readonly: false, caseSensitive: true, atomicRename: false, // S3 doesn't support atomic rename supportsWatch: false, // S3 doesn't support real-time watch supportsMetadata: true, supportsGlob: false, // S3 não tem suporte nativo para glob (é simulado via list + filter) maxFileSize: 5 * 1024 * 1024 * 1024 * 1024, // 5TB with multipart upload maxPathLength: 1024, description: "AWS S3 cloud storage with eventual consistency. Best for large files, backups, and static content.", }; } async validateConfiguration( config: any, ): Promise<{ valid: boolean; errors?: string[] }> { const errors: string[] = []; if (!config) { errors.push("Configuration is required"); return { valid: false, errors }; } // Validar bucket (obrigatório) if (config.bucket === undefined || config.bucket === null) { errors.push("bucket is required"); } else if (typeof config.bucket !== "string") { errors.push("bucket must be a string"); } else if (config.bucket.trim() === "") { errors.push("bucket cannot be empty"); } else if (!/^[a-z0-9.-]+$/.test(config.bucket)) { errors.push( "bucket name must contain only lowercase letters, numbers, dots, and hyphens", ); } // Validar region (obrigatório) if (config.region === undefined || config.region === null) { errors.push("region is required"); } else if (typeof config.region !== "string") { errors.push("region must be a string"); } else if (config.region.trim() === "") { errors.push("region cannot be empty"); } // Validar prefix (opcional) if (config.prefix !== undefined) { if (typeof config.prefix !== "string") { errors.push("prefix must be a string"); } else if (config.prefix.length > 1000) { errors.push("prefix is too long (max 1000 characters)"); } } // Validar mode (opcional) if (config.mode !== undefined) { if (config.mode !== "strict" && config.mode !== "lenient") { errors.push("mode must be 'strict' or 'lenient'"); } } // Validar credentials (opcional) if (config.credentials !== undefined) { if (typeof config.credentials !== "object") { errors.push("credentials must be an object"); } else { if ( !config.credentials.accessKeyId || typeof config.credentials.accessKeyId !== "string" ) { errors.push("credentials.accessKeyId must be a string"); } if ( !config.credentials.secretAccessKey || typeof config.credentials.secretAccessKey !== "string" ) { errors.push("credentials.secretAccessKey must be a string"); } } } // Validar clientOptions (opcional) if (config.clientOptions !== undefined) { if (typeof config.clientOptions !== "object") { errors.push("clientOptions must be an object"); } else { if ( config.clientOptions.endpoint !== undefined && typeof config.clientOptions.endpoint !== "string" ) { errors.push("clientOptions.endpoint must be a string"); } if ( config.clientOptions.forcePathStyle !== undefined && typeof config.clientOptions.forcePathStyle !== "boolean" ) { errors.push("clientOptions.forcePathStyle must be a boolean"); } } } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, }; } async dispose(fs: IReactiveFileSystem): Promise<void> { if ("dispose" in fs && typeof fs.dispose === "function") { await fs.dispose(); } } } // Export singleton instance export const s3Provider = new S3WorkspaceProvider();