From 06ca6e38b3c766ea76658297ebadb44e9348e547 Mon Sep 17 00:00:00 2001 From: Owais Rizvi <68285890+owaisrizvi97@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:54:58 +0500 Subject: [PATCH] feat: add IRSA support for S3 authentication (#265) --- README.md | 4 +- apps/web/package.json | 1 - apps/web/src/pages/api/upload/image.ts | 14 ++-- packages/api/src/utils/s3.ts | 13 ++-- packages/auth/src/auth.ts | 13 ++-- pnpm-lock.yaml | 97 -------------------------- 6 files changed, 29 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index a43c422c..d09d1742 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,8 @@ pnpm dev | `TRELLO_APP_API_SECRET` | Trello app API secret | For Trello import | `xxx` | | `S3_REGION` | S3 storage region | For file uploads | `WEUR` | | `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` | -| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads | `xxx` | -| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads | `xxx` | +| `S3_ACCESS_KEY_ID` | S3 access key | For file uploads (optional with IRSA) | `xxx` | +| `S3_SECRET_ACCESS_KEY` | S3 secret key | For file uploads (optional with IRSA) | `xxx` | | `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 | For file uploads | `true` | | `NEXT_PUBLIC_STORAGE_URL` | Storage service URL | For file uploads | `https://storage.kanbn.com` | | `NEXT_PUBLIC_STORAGE_DOMAIN` | Storage domain name | For file uploads | `kanbn.com` | diff --git a/apps/web/package.json b/apps/web/package.json index 4cc2ab49..9f5fbd47 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -44,7 +44,6 @@ "@trpc/next": "^11.0.0-rc.660", "@trpc/react-query": "catalog:", "@trpc/server": "catalog:", - "aws-sdk": "^2.1692.0", "date-fns": "^4.1.0", "geist": "^1.3.1", "jose": "^6.1.2", diff --git a/apps/web/src/pages/api/upload/image.ts b/apps/web/src/pages/api/upload/image.ts index 2633023a..e250bbd6 100644 --- a/apps/web/src/pages/api/upload/image.ts +++ b/apps/web/src/pages/api/upload/image.ts @@ -43,18 +43,22 @@ export default async function handler( return res.status(400).json({ error: "Invalid content type" }); } + const credentials = + env.S3_ACCESS_KEY_ID && env.S3_SECRET_ACCESS_KEY + ? { + accessKeyId: env.S3_ACCESS_KEY_ID, + secretAccessKey: env.S3_SECRET_ACCESS_KEY, + } + : undefined; + const client = new S3Client({ region: env.S3_REGION ?? "", endpoint: env.S3_ENDPOINT ?? "", forcePathStyle: env.S3_FORCE_PATH_STYLE === "true", - credentials: { - accessKeyId: env.S3_ACCESS_KEY_ID ?? "", - secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "", - }, + credentials, }); const signedUrl = await getSignedUrl( - // @ts-ignore client, new PutObjectCommand({ Bucket: nextRuntimeEnv("NEXT_PUBLIC_AVATAR_BUCKET_NAME") ?? "", diff --git a/packages/api/src/utils/s3.ts b/packages/api/src/utils/s3.ts index 961bb33c..8b5bdcbc 100644 --- a/packages/api/src/utils/s3.ts +++ b/packages/api/src/utils/s3.ts @@ -6,14 +6,19 @@ import { import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; export function createS3Client() { + const credentials = + process.env.S3_ACCESS_KEY_ID && process.env.S3_SECRET_ACCESS_KEY + ? { + accessKeyId: process.env.S3_ACCESS_KEY_ID, + secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, + } + : undefined; + return new S3Client({ region: process.env.S3_REGION ?? "", endpoint: process.env.S3_ENDPOINT ?? "", forcePathStyle: process.env.S3_FORCE_PATH_STYLE === "true", - credentials: { - accessKeyId: process.env.S3_ACCESS_KEY_ID ?? "", - secretAccessKey: process.env.S3_SECRET_ACCESS_KEY ?? "", - }, + credentials, }); } diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index 497cf614..a55e5c0a 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -384,14 +384,19 @@ export const initAuth = (db: dbClient) => { !user.image.includes(process.env.NEXT_PUBLIC_STORAGE_DOMAIN!) ) { try { + const credentials = + env("S3_ACCESS_KEY_ID") && env("S3_SECRET_ACCESS_KEY") + ? { + accessKeyId: env("S3_ACCESS_KEY_ID")!, + secretAccessKey: env("S3_SECRET_ACCESS_KEY")!, + } + : undefined; + const client = new S3Client({ region: env("S3_REGION") ?? "", endpoint: env("S3_ENDPOINT") ?? "", forcePathStyle: env("S3_FORCE_PATH_STYLE") === "true", - credentials: { - accessKeyId: env("S3_ACCESS_KEY_ID") ?? "", - secretAccessKey: env("S3_SECRET_ACCESS_KEY") ?? "", - }, + credentials, }); const allowedFileExtensions = ["jpg", "jpeg", "png", "webp"]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fe6aa7c4..84dfd580 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,9 +160,6 @@ importers: '@trpc/server': specifier: 'catalog:' version: 11.5.0(typescript@5.9.2) - aws-sdk: - specifier: ^2.1692.0 - version: 2.1692.0 date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -3966,10 +3963,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-sdk@2.1692.0: - resolution: {integrity: sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==} - engines: {node: '>= 10.0.0'} - axe-core@4.10.3: resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} engines: {node: '>=4'} @@ -4074,9 +4067,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@4.9.2: - resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -4911,10 +4901,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - events@1.1.1: - resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} - engines: {node: '>=0.4.x'} - events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -5245,9 +5231,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - ieee754@1.1.13: - resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -5325,10 +5308,6 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -5515,9 +5494,6 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -5559,10 +5535,6 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jmespath@0.16.0: - resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} - engines: {node: '>= 0.6.0'} - jose@5.10.0: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} @@ -6753,9 +6725,6 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} - punycode@1.3.2: - resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6771,11 +6740,6 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - querystring@0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -7703,9 +7667,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url@0.10.3: - resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} - use-memo-one@1.1.3: resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} peerDependencies: @@ -7719,17 +7680,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - uuid@8.0.0: - resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -11811,19 +11765,6 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - aws-sdk@2.1692.0: - dependencies: - buffer: 4.9.2 - events: 1.1.1 - ieee754: 1.1.13 - jmespath: 0.16.0 - querystring: 0.2.0 - sax: 1.2.1 - url: 0.10.3 - util: 0.12.5 - uuid: 8.0.0 - xml2js: 0.6.2 - axe-core@4.10.3: {} axios@1.11.0: @@ -11947,12 +11888,6 @@ snapshots: buffer-from@1.1.2: {} - buffer@4.9.2: - dependencies: - base64-js: 1.5.1 - ieee754: 1.1.13 - isarray: 1.0.0 - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -12925,8 +12860,6 @@ snapshots: esutils@2.0.3: {} - events@1.1.1: {} - events@3.3.0: {} execa@5.1.1: @@ -13338,8 +13271,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - ieee754@1.1.13: {} - ieee754@1.2.1: {} ignore@5.3.2: {} @@ -13440,11 +13371,6 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -13608,8 +13534,6 @@ snapshots: dependencies: is-docker: 2.2.1 - isarray@1.0.0: {} - isarray@2.0.5: {} isbinaryfile@4.0.10: {} @@ -13660,8 +13584,6 @@ snapshots: jiti@1.21.7: {} - jmespath@0.16.0: {} - jose@5.10.0: {} jose@6.1.2: {} @@ -15236,8 +15158,6 @@ snapshots: punycode.js@2.3.1: {} - punycode@1.3.2: {} - punycode@2.3.1: {} pvtsutils@1.3.6: @@ -15250,8 +15170,6 @@ snapshots: dependencies: side-channel: 1.1.0 - querystring@0.2.0: {} - queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -16435,11 +16353,6 @@ snapshots: dependencies: punycode: 2.3.1 - url@0.10.3: - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - use-memo-one@1.1.3(react@18.3.1): dependencies: react: 18.3.1 @@ -16450,18 +16363,8 @@ snapshots: util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.0 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - uuid@11.1.0: {} - uuid@8.0.0: {} - uuid@9.0.1: {} uvu@0.5.6: