{"_id":"@davicruzof/r2-storage","_rev":"2-52ed3f72e3e581d52f8cd9bcf4d8a9eb","name":"@davicruzof/r2-storage","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@davicruzof/r2-storage","version":"1.0.0","keywords":["nestjs","r2","cloudflare","s3","storage","upload"],"author":"","license":"MIT","_id":"@davicruzof/r2-storage@1.0.0","maintainers":[{"name":"davicruzof","email":"daviondejesus@gmail.com"}],"dist":{"shasum":"e738c4bb73bce47f917525b32343ec5d56b37d79","tarball":"https://registry.npmjs.org/@davicruzof/r2-storage/-/r2-storage-1.0.0.tgz","fileCount":17,"integrity":"sha512-Ii8L20WXU08N34g/4vKyy5ojSOmWRiufwemAnTYCKLxkyGxct+ZUJ7Dc13A7h1yGkw1UfPeTSCXjkWO9SQ1CLA==","signatures":[{"sig":"MEUCIC5zK3JcbreUvCjni0oDVYogPcqQyQeKjL9myL7RKhQEAiEA/BPoiKuVrZH/ugCT/iTEKJmXaoGJcsIgMKB/YXuZbn0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16730},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"scripts":{"build":"tsc -p tsconfig.lib.json","prepublishOnly":"npm run build"},"_npmUser":{"name":"davicruzof","email":"daviondejesus@gmail.com"},"repository":{"url":"","type":"git"},"_npmVersion":"11.6.2","description":"Cloudflare R2 (API S3-compatível) – upload de imagens e arquivos","directories":{},"_nodeVersion":"24.13.0","dependencies":{"@aws-sdk/client-s3":"^3.0.0"},"_hasShrinkwrap":false,"peerDependencies":{"@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0"},"_npmOperationalInternal":{"tmp":"tmp/r2-storage_1.0.0_1773013919681_0.0034070196493620752","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@davicruzof/r2-storage","version":"1.1.0","description":"Cloudflare R2 (API S3-compatível) – upload e remoção de imagens e arquivos","author":"","license":"MIT","keywords":["nestjs","r2","cloudflare","s3","storage","upload","delete"],"repository":{"type":"git","url":""},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p tsconfig.lib.json","prepublishOnly":"npm run build"},"dependencies":{"@aws-sdk/client-s3":"^3.0.0"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0"},"engines":{"node":">=18"},"devDependencies":{"@types/node":"^25.3.5","typescript":"^5.9.3"},"gitHead":"e6fa5484848b18561189dd7bd39ce81856cfc15b","_id":"@davicruzof/r2-storage@1.1.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-hG0mFZH9BO9XjpMcwMSlypGmJCf385FCM08vUT3HLyCkA/+Q9xNGUKDhp0YLeJxKdRBjHQ9CAoSIzBVGLfi2hw==","shasum":"70e834d302a55e4e16c78bd1ddf13b345df6f651","tarball":"https://registry.npmjs.org/@davicruzof/r2-storage/-/r2-storage-1.1.0.tgz","fileCount":12,"unpackedSize":19164,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDdpdaa3L3vvz6s8qUKhg/WMNa4D73dZwc26WqSBhr6MAiApTGhy129+VMf2Qd5FYpdiMTi9RM1wPM7AwZ0z/T/7tA=="}]},"_npmUser":{"name":"davicruzof","email":"daviondejesus@gmail.com"},"directories":{},"maintainers":[{"name":"davicruzof","email":"daviondejesus@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/r2-storage_1.1.0_1773015437257_0.17321363632355347"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-08T23:51:59.571Z","modified":"2026-03-09T00:17:17.563Z","1.0.0":"2026-03-08T23:51:59.826Z","1.1.0":"2026-03-09T00:17:17.423Z"},"license":"MIT","keywords":["nestjs","r2","cloudflare","s3","storage","upload","delete"],"repository":{"type":"git","url":""},"description":"Cloudflare R2 (API S3-compatível) – upload e remoção de imagens e arquivos","maintainers":[{"name":"davicruzof","email":"daviondejesus@gmail.com"}],"readme":"# @davicruzof/r2-storage\n\nMódulo para **Cloudflare R2** (API compatível com S3): upload e remoção de imagens/arquivos, URLs públicas e injeção do cliente S3.\n\n## Requisitos\n\n- Node.js >= 18\n- NestJS 10 ou 11\n- Conta Cloudflare com R2 e bucket criado\n\n## Instalação\n\n```bash\nnpm install @davicruzof/r2-storage\n```\n\n```bash\nyarn add @davicruzof/r2-storage\n```\n\nO cliente S3 (`@aws-sdk/client-s3`) é instalado automaticamente como dependência.\n\n## Configuração\n\n### forRoot (síncrono)\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { R2Module } from \"@davicruzof/r2-storage\";\n\n@Module({\n  imports: [\n    R2Module.forRoot({\n      accountId: process.env.R2_ACCOUNT_ID!,\n      accessKeyId: process.env.R2_ACCESS_KEY_ID!,\n      secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,\n      bucketName: process.env.R2_BUCKET_NAME!,\n      publicBaseUrl: process.env.R2_PUBLIC_URL, // opcional\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### forRootAsync (assíncrono)\n\nÚtil com `ConfigService` ou outras fontes de configuração:\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { ConfigModule, ConfigService } from \"@nestjs/config\";\nimport { R2Module } from \"@davicruzof/r2-storage\";\n\n@Module({\n  imports: [\n    R2Module.forRootAsync({\n      inject: [ConfigService],\n      useFactory: (config: ConfigService) => ({\n        accountId: config.getOrThrow(\"R2_ACCOUNT_ID\"),\n        accessKeyId: config.getOrThrow(\"R2_ACCESS_KEY_ID\"),\n        secretAccessKey: config.getOrThrow(\"R2_SECRET_ACCESS_KEY\"),\n        bucketName: config.getOrThrow(\"R2_BUCKET_NAME\"),\n        publicBaseUrl: config.get(\"R2_PUBLIC_URL\"),\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n## Uso\n\nInjete `R2StorageService` em controllers ou outros services:\n\n```typescript\nimport {\n  Body,\n  Controller,\n  Delete,\n  Post,\n  UseInterceptors,\n  UploadedFile,\n} from \"@nestjs/common\";\nimport { FileInterceptor } from \"@nestjs/platform-express\";\nimport { R2StorageService } from \"@davicruzof/r2-storage\";\n\n@Controller(\"upload\")\nexport class UploadController {\n  constructor(private readonly r2: R2StorageService) {}\n\n  @Post(\"image\")\n  @UseInterceptors(FileInterceptor(\"file\"))\n  async upload(@UploadedFile() file: Express.Multer.File) {\n    const path = `images/${Date.now()}-${file.originalname}`;\n    const result = await this.r2.uploadImage(file.buffer, path, {\n      contentType: file.mimetype,\n    });\n    return result; // { path, bucket }\n  }\n\n  @Post(\"image-with-url\")\n  @UseInterceptors(FileInterceptor(\"file\"))\n  async uploadWithUrl(@UploadedFile() file: Express.Multer.File) {\n    const path = `images/${Date.now()}-${file.originalname}`;\n    const result = await this.r2.uploadImageAndGetPublicUrl(file.buffer, path, {\n      contentType: file.mimetype,\n    });\n    return result; // { path, bucket, publicUrl }\n  }\n\n  @Delete(\"image\")\n  async deleteImage(@Body(\"path\") path: string) {\n    await this.r2.remove(path);\n    return { ok: true };\n  }\n}\n```\n\n### Métodos do R2StorageService\n\n| Método                                               | Descrição                                                                                                  |\n| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| `uploadImage(buffer, path, options?)`                | Envia um buffer para o bucket. Retorna `{ path, bucket }`.                                                 |\n| `getPublicUrl(path)`                                 | Monta a URL pública do arquivo (requer `publicBaseUrl` configurado).                                       |\n| `uploadImageAndGetPublicUrl(buffer, path, options?)` | Faz o upload e retorna `{ path, bucket, publicUrl }`.                                                      |\n| `remove(path)`                                       | Remove um objeto do bucket pelo path (key).                                                                |\n| `removeMany(paths)`                                  | Remove vários objetos de uma vez. Retorna `{ deleted: string[], errors: Array<{ path, code, message }> }`. |\n\n### Remoção\n\n- **`remove(path)`** — Remove um único arquivo. Path é o mesmo usado no upload (ex: `images/abc123.jpg`).\n- **`removeMany(paths)`** — Remove vários arquivos em uma chamada. Retorna `deleted` (paths removidos) e `errors` (falhas por key). Paths duplicados são ignorados.\n\n### Opções de upload\n\n- `contentType` (opcional): MIME type (ex: `image/jpeg`, `image/png`). Padrão: `image/jpeg`.\n\n### Opções do módulo (R2ModuleOptions)\n\n| Campo             | Obrigatório | Descrição                                                              |\n| ----------------- | ----------- | ---------------------------------------------------------------------- |\n| `accountId`       | Sim         | ID da conta Cloudflare                                                 |\n| `accessKeyId`     | Sim         | Access Key do R2 (Manage R2 API Tokens)                                |\n| `secretAccessKey` | Sim         | Secret Access Key do R2                                                |\n| `bucketName`      | Sim         | Nome do bucket                                                         |\n| `publicBaseUrl`   | Não         | URL base pública (ex: `https://pub-xxx.r2.dev` ou domínio customizado) |\n\n## Licença\n\nMIT\n","readmeFilename":"README.md"}