From 6a48830083e90c39c9e168fb7637a143254cb8e8 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 00:14:06 +0900 Subject: [PATCH 001/167] =?UTF-8?q?feat:=20object=20storage=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=ED=8C=8C=EC=9D=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/package.json | 3 +- apps/media/src/ncp/ncp.config.ts | 25 + apps/media/src/ncp/ncp.module.ts | 10 + apps/media/src/ncp/ncp.service.ts | 45 + packages/types/src/errorMessages.ts | 1 + pnpm-lock.yaml | 1225 +++++++++++++++++++++++++++ 6 files changed, 1308 insertions(+), 1 deletion(-) create mode 100644 apps/media/src/ncp/ncp.config.ts create mode 100644 apps/media/src/ncp/ncp.module.ts create mode 100644 apps/media/src/ncp/ncp.service.ts diff --git a/apps/media/package.json b/apps/media/package.json index cdbe2361..96fcfebd 100644 --- a/apps/media/package.json +++ b/apps/media/package.json @@ -20,14 +20,15 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@aws-sdk/client-s3": "^3.700.0", "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.4.7", "@nestjs/websockets": "^10.4.7", - "@repo/tsconfig": "workspace:*", "@repo/mediasoup": "workspace:*", + "@repo/tsconfig": "workspace:*", "@repo/types": "workspace:*", "mediasoup": "^3.15.0", "reflect-metadata": "^0.2.0", diff --git a/apps/media/src/ncp/ncp.config.ts b/apps/media/src/ncp/ncp.config.ts new file mode 100644 index 00000000..b1b9c591 --- /dev/null +++ b/apps/media/src/ncp/ncp.config.ts @@ -0,0 +1,25 @@ +import { S3Client } from '@aws-sdk/client-s3'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class NcpConfig { + s3Client: S3Client; + + constructor(private configService: ConfigService) { + const accessKeyId = this.configService.get('NCP_ACCESS_KEY'); + const secretAccessKey = this.configService.get('NCP_SECRET_KEY'); + const region = this.configService.get('NCP_OBJECT_STORAGE_REGION'); + const endpoint = this.configService.get('NCP_OBJECT_STORAGE_ENDPOINT'); + + this.s3Client = new S3Client({ + region: region, + credentials: { + accessKeyId: accessKeyId, + secretAccessKey: secretAccessKey, + }, + endpoint: endpoint, + forcePathStyle: true, + }); + } +} diff --git a/apps/media/src/ncp/ncp.module.ts b/apps/media/src/ncp/ncp.module.ts new file mode 100644 index 00000000..2b639e97 --- /dev/null +++ b/apps/media/src/ncp/ncp.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { NcpConfig } from './ncp.config'; +import { NcpService } from './ncp.service'; + +@Module({ + providers: [NcpService, NcpConfig], + exports: [NcpService], +}) +export class AppModule {} diff --git a/apps/media/src/ncp/ncp.service.ts b/apps/media/src/ncp/ncp.service.ts new file mode 100644 index 00000000..54dc4e8a --- /dev/null +++ b/apps/media/src/ncp/ncp.service.ts @@ -0,0 +1,45 @@ +import * as fs from 'fs'; + +import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { ErrorMessage } from '@repo/types'; + +import { NcpConfig } from './ncp.config'; + +@Injectable() +export class NcpService { + private s3: S3Client; + private endpoint: string; + + constructor( + private ncpConfig: NcpConfig, + private configService: ConfigService + ) { + this.s3 = ncpConfig.s3Client; + } + + async uploadFile(localFilePath: string, remoteFileName: string): Promise { + const bucketName = this.configService.get('NCP_OBJECT_STORAGE_BUCKET'); + const endpoint = this.configService.get('NCP_OBJECT_STORAGE_ENDPOINT'); + + const fileStream = fs.createReadStream(localFilePath); + const params = { + Bucket: bucketName, + Key: remoteFileName, + Body: fileStream, + }; + + try { + const uploadResponse = await this.s3.send(new PutObjectCommand(params)); + // console.log('File uploaded:', uploadResponse); + + const url = `${endpoint}/${bucketName}/${remoteFileName}`; + // console.log('Uploaded file URL:', url); + + return remoteFileName; + } catch (error) { + throw new Error(ErrorMessage.FILE_UPLOAD_FAILED); + } + } +} diff --git a/packages/types/src/errorMessages.ts b/packages/types/src/errorMessages.ts index b4aa65cd..686c5f28 100644 --- a/packages/types/src/errorMessages.ts +++ b/packages/types/src/errorMessages.ts @@ -20,6 +20,7 @@ export const ErrorMessage = { ROOM_NOT_FOUND: '방이 존재하지 않습니다', TRANSPORT_NOT_FOUND: 'transport가 존재하지 않습니다', PEER_ALREADY_EXISTS_IN_ROOM: '이미 방에 존재하는 Peer입니다', + FILE_UPLOAD_FAILED: '파일 업로드에 실패했습니다', } as const; export type ErrorMessage = (typeof ErrorMessage)[keyof typeof ErrorMessage]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39481dd6..626b1c34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,6 +195,9 @@ importers: apps/media: dependencies: + '@aws-sdk/client-s3': + specifier: ^3.700.0 + version: 3.700.0 '@nestjs/common': specifier: ^10.0.0 version: 10.4.6(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -566,6 +569,169 @@ packages: resolution: {integrity: sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==} engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-s3@3.700.0': + resolution: {integrity: sha512-TZTc8OZ873VodJNcsQ4Y/60f0Y0Ws5Z3Xm5QBgPCvhqQS7/+V28pXM3fAomJsKDWTxzH0nP9csX5cPvLybgdZQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.699.0': + resolution: {integrity: sha512-u8a1GorY5D1l+4FQAf4XBUC1T10/t7neuwT21r0ymrtMFSK2a9QqVHKMoLkvavAwyhJnARSBM9/UQC797PFOFw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.699.0 + + '@aws-sdk/client-sso@3.696.0': + resolution: {integrity: sha512-q5TTkd08JS0DOkHfUL853tuArf7NrPeqoS5UOvqJho8ibV9Ak/a/HO4kNvy9Nj3cib/toHYHsQIEtecUPSUUrQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.699.0': + resolution: {integrity: sha512-++lsn4x2YXsZPIzFVwv3fSUVM55ZT0WRFmPeNilYIhZClxHLmVAWKH4I55cY9ry60/aTKYjzOXkWwyBKGsGvQg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.696.0': + resolution: {integrity: sha512-3c9III1k03DgvRZWg8vhVmfIXPG6hAciN9MzQTzqGngzWAELZF/WONRTRQuDFixVtarQatmLHYVw/atGeA2Byw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.696.0': + resolution: {integrity: sha512-T9iMFnJL7YTlESLpVFT3fg1Lkb1lD+oiaIC8KMpepb01gDUBIpj9+Y+pA/cgRWW0yRxmkDXNazAE2qQTVFGJzA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.696.0': + resolution: {integrity: sha512-GV6EbvPi2eq1+WgY/o2RFA3P7HGmnkIzCNmhwtALFlqMroLYWKE7PSeHw66Uh1dFQeVESn0/+hiUNhu1mB0emA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-ini@3.699.0': + resolution: {integrity: sha512-dXmCqjJnKmG37Q+nLjPVu22mNkrGHY8hYoOt3Jo9R2zr5MYV7s/NHsCHr+7E+BZ+tfZYLRPeB1wkpTeHiEcdRw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.699.0 + + '@aws-sdk/credential-provider-node@3.699.0': + resolution: {integrity: sha512-MmEmNDo1bBtTgRmdNfdQksXu4uXe66s0p1hi1YPrn1h59Q605eq/xiWbGL6/3KdkViH6eGUuABeV2ODld86ylg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.696.0': + resolution: {integrity: sha512-mL1RcFDe9sfmyU5K1nuFkO8UiJXXxLX4JO1gVaDIOvPqwStpUAwi3A1BoeZhWZZNQsiKI810RnYGo0E0WB/hUA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.699.0': + resolution: {integrity: sha512-Ekp2cZG4pl9D8+uKWm4qO1xcm8/MeiI8f+dnlZm8aQzizeC+aXYy9GyoclSf6daK8KfRPiRfM7ZHBBL5dAfdMA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.696.0': + resolution: {integrity: sha512-XJ/CVlWChM0VCoc259vWguFUjJDn/QwDqHwbx+K9cg3v6yrqXfK5ai+p/6lx0nQpnk4JzPVeYYxWRpaTsGC9rg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.696.0 + + '@aws-sdk/middleware-bucket-endpoint@3.696.0': + resolution: {integrity: sha512-V07jishKHUS5heRNGFpCWCSTjRJyQLynS/ncUeE8ZYtG66StOOQWftTwDfFOSoXlIqrXgb4oT9atryzXq7Z4LQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-expect-continue@3.696.0': + resolution: {integrity: sha512-vpVukqY3U2pb+ULeX0shs6L0aadNep6kKzjme/MyulPjtUDJpD3AekHsXRrCCGLmOqSKqRgQn5zhV9pQhHsb6Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.697.0': + resolution: {integrity: sha512-K/y43P+NuHu5+21/29BoJSltcPekvcCU8i74KlGGHbW2Z105e5QVZlFjxivcPOjOA3gdC0W4SoFSIWam5RBhzw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.696.0': + resolution: {integrity: sha512-zELJp9Ta2zkX7ELggMN9qMCgekqZhFC5V2rOr4hJDEb/Tte7gpfKSObAnw/3AYiVqt36sjHKfdkoTsuwGdEoDg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-location-constraint@3.696.0': + resolution: {integrity: sha512-FgH12OB0q+DtTrP2aiDBddDKwL4BPOrm7w3VV9BJrSdkqQCNBPz8S1lb0y5eVH4tBG+2j7gKPlOv1wde4jF/iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.696.0': + resolution: {integrity: sha512-KhkHt+8AjCxcR/5Zp3++YPJPpFQzxpr+jmONiT/Jw2yqnSngZ0Yspm5wGoRx2hS1HJbyZNuaOWEGuJoxLeBKfA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.696.0': + resolution: {integrity: sha512-si/maV3Z0hH7qa99f9ru2xpS5HlfSVcasRlNUXKSDm611i7jFMWwGNLUOXFAOLhXotPX5G3Z6BLwL34oDeBMug==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.696.0': + resolution: {integrity: sha512-M7fEiAiN7DBMHflzOFzh1I2MNSlLpbiH2ubs87bdRc2wZsDPSbs4l3v6h3WLhxoQK0bq6vcfroudrLBgvCuX3Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-ssec@3.696.0': + resolution: {integrity: sha512-w/d6O7AOZ7Pg3w2d3BxnX5RmGNWb5X4RNxF19rJqcgu/xqxxE/QwZTNd5a7eTsqLXAUIfbbR8hh0czVfC1pJLA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.696.0': + resolution: {integrity: sha512-Lvyj8CTyxrHI6GHd2YVZKIRI5Fmnugt3cpJo0VrKKEgK5zMySwEZ1n4dqPK6czYRWKd5+WnYHYAuU+Wdk6Jsjw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.696.0': + resolution: {integrity: sha512-7EuH142lBXjI8yH6dVS/CZeiK/WZsmb/8zP6bQbVYpMrppSTgB3MzZZdxVZGzL5r8zPQOU10wLC4kIMy0qdBVQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.696.0': + resolution: {integrity: sha512-ijPkoLjXuPtgxAYlDoYls8UaG/VKigROn9ebbvPL/orEY5umedd3iZTcS9T+uAf4Ur3GELLxMQiERZpfDKaz3g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/token-providers@3.699.0': + resolution: {integrity: sha512-kuiEW9DWs7fNos/SM+y58HCPhcIzm1nEZLhe2/7/6+TvAYLuEWURYsbK48gzsxXlaJ2k/jGY3nIsA7RptbMOwA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.699.0 + + '@aws-sdk/types@3.696.0': + resolution: {integrity: sha512-9rTvUJIAj5d3//U5FDPWGJ1nFJLuWb30vugGOrWk7aNZ6y9tuA3PI7Cc9dP8WEXKVyK1vuuk8rSFP2iqXnlgrw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-arn-parser@3.693.0': + resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.696.0': + resolution: {integrity: sha512-T5s0IlBVX+gkb9g/I6CLt4yAZVzMSiGnbUqWihWsHvQR1WOoIcndQy/Oz/IJXT9T2ipoy7a80gzV6a5mglrioA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-locate-window@3.693.0': + resolution: {integrity: sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-user-agent-browser@3.696.0': + resolution: {integrity: sha512-Z5rVNDdmPOe6ELoM5AhF/ja5tSjbe6ctSctDPb0JdDf4dT0v2MfwhJKzXju2RzX8Es/77Glh7MlaXLE0kCB9+Q==} + + '@aws-sdk/util-user-agent-node@3.696.0': + resolution: {integrity: sha512-KhKqcfyXIB0SCCt+qsu4eJjsfiOrNzK5dCV7RAW2YIpp+msxGUUX0NdRE9rkzjiv+3EMktgJm3eEIS+yxtlVdQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.696.0': + resolution: {integrity: sha512-dn1mX+EeqivoLYnY7p2qLrir0waPnCgS/0YdRCAVU2x14FgfUYCH6Im3w3oi2dMwhxfKY5lYVB5NKvZu7uI9lQ==} + engines: {node: '>=16.0.0'} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -1711,6 +1877,209 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@smithy/abort-controller@3.1.8': + resolution: {integrity: sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@3.0.1': + resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + + '@smithy/chunked-blob-reader@4.0.0': + resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + + '@smithy/config-resolver@3.0.12': + resolution: {integrity: sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.5.4': + resolution: {integrity: sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.7': + resolution: {integrity: sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-codec@3.1.9': + resolution: {integrity: sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==} + + '@smithy/eventstream-serde-browser@3.0.13': + resolution: {integrity: sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.10': + resolution: {integrity: sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@3.0.12': + resolution: {integrity: sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@3.0.12': + resolution: {integrity: sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@4.1.1': + resolution: {integrity: sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==} + + '@smithy/hash-blob-browser@3.1.9': + resolution: {integrity: sha512-wOu78omaUuW5DE+PVWXiRKWRZLecARyP3xcq5SmkXUw9+utgN8HnSnBfrjL2B/4ZxgqPjaAJQkC/+JHf1ITVaQ==} + + '@smithy/hash-node@3.0.10': + resolution: {integrity: sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@3.1.9': + resolution: {integrity: sha512-3XfHBjSP3oDWxLmlxnt+F+FqXpL3WlXs+XXaB6bV9Wo8BBu87fK1dSEsyH7Z4ZHRmwZ4g9lFMdf08m9hoX1iRA==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@3.0.10': + resolution: {integrity: sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@3.0.10': + resolution: {integrity: sha512-m3bv6dApflt3fS2Y1PyWPUtRP7iuBlvikEOGwu0HsCZ0vE7zcIX+dBoh3e+31/rddagw8nj92j0kJg2TfV+SJA==} + + '@smithy/middleware-content-length@3.0.12': + resolution: {integrity: sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.2.4': + resolution: {integrity: sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.28': + resolution: {integrity: sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@3.0.10': + resolution: {integrity: sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@3.0.10': + resolution: {integrity: sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.11': + resolution: {integrity: sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.3.1': + resolution: {integrity: sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@3.1.10': + resolution: {integrity: sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.7': + resolution: {integrity: sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@3.0.10': + resolution: {integrity: sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@3.0.10': + resolution: {integrity: sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@3.0.10': + resolution: {integrity: sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.11': + resolution: {integrity: sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.2.3': + resolution: {integrity: sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.4.5': + resolution: {integrity: sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==} + engines: {node: '>=16.0.0'} + + '@smithy/types@3.7.1': + resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@3.0.10': + resolution: {integrity: sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.28': + resolution: {integrity: sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.28': + resolution: {integrity: sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.1.6': + resolution: {integrity: sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@3.0.10': + resolution: {integrity: sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@3.0.10': + resolution: {integrity: sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.3.1': + resolution: {integrity: sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@3.1.9': + resolution: {integrity: sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==} + engines: {node: '>=16.0.0'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -2645,6 +3014,9 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -3456,6 +3828,10 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -5417,6 +5793,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -6161,6 +6540,513 @@ snapshots: transitivePeerDependencies: - chokidar + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.696.0 + tslib: 2.8.0 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.696.0 + tslib: 2.8.0 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-locate-window': 3.693.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-locate-window': 3.693.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.696.0 + tslib: 2.8.0 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.0 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-sdk/client-s3@3.700.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.699.0(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/client-sts': 3.699.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/credential-provider-node': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/middleware-bucket-endpoint': 3.696.0 + '@aws-sdk/middleware-expect-continue': 3.696.0 + '@aws-sdk/middleware-flexible-checksums': 3.697.0 + '@aws-sdk/middleware-host-header': 3.696.0 + '@aws-sdk/middleware-location-constraint': 3.696.0 + '@aws-sdk/middleware-logger': 3.696.0 + '@aws-sdk/middleware-recursion-detection': 3.696.0 + '@aws-sdk/middleware-sdk-s3': 3.696.0 + '@aws-sdk/middleware-ssec': 3.696.0 + '@aws-sdk/middleware-user-agent': 3.696.0 + '@aws-sdk/region-config-resolver': 3.696.0 + '@aws-sdk/signature-v4-multi-region': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-endpoints': 3.696.0 + '@aws-sdk/util-user-agent-browser': 3.696.0 + '@aws-sdk/util-user-agent-node': 3.696.0 + '@aws-sdk/xml-builder': 3.696.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/eventstream-serde-browser': 3.0.13 + '@smithy/eventstream-serde-config-resolver': 3.0.10 + '@smithy/eventstream-serde-node': 3.0.12 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-blob-browser': 3.1.9 + '@smithy/hash-node': 3.0.10 + '@smithy/hash-stream-node': 3.1.9 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/md5-js': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-stream': 3.3.1 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.1.9 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sts': 3.699.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/credential-provider-node': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/middleware-host-header': 3.696.0 + '@aws-sdk/middleware-logger': 3.696.0 + '@aws-sdk/middleware-recursion-detection': 3.696.0 + '@aws-sdk/middleware-user-agent': 3.696.0 + '@aws-sdk/region-config-resolver': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-endpoints': 3.696.0 + '@aws-sdk/util-user-agent-browser': 3.696.0 + '@aws-sdk/util-user-agent-node': 3.696.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.696.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/middleware-host-header': 3.696.0 + '@aws-sdk/middleware-logger': 3.696.0 + '@aws-sdk/middleware-recursion-detection': 3.696.0 + '@aws-sdk/middleware-user-agent': 3.696.0 + '@aws-sdk/region-config-resolver': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-endpoints': 3.696.0 + '@aws-sdk/util-user-agent-browser': 3.696.0 + '@aws-sdk/util-user-agent-node': 3.696.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.699.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.699.0(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/core': 3.696.0 + '@aws-sdk/credential-provider-node': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/middleware-host-header': 3.696.0 + '@aws-sdk/middleware-logger': 3.696.0 + '@aws-sdk/middleware-recursion-detection': 3.696.0 + '@aws-sdk/middleware-user-agent': 3.696.0 + '@aws-sdk/region-config-resolver': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-endpoints': 3.696.0 + '@aws-sdk/util-user-agent-browser': 3.696.0 + '@aws-sdk/util-user-agent-node': 3.696.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/core': 2.5.4 + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 + fast-xml-parser: 4.4.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-env@3.696.0': + dependencies: + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-http@3.696.0': + dependencies: + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/node-http-handler': 3.3.1 + '@smithy/property-provider': 3.1.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-stream': 3.3.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-ini@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0)': + dependencies: + '@aws-sdk/client-sts': 3.699.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/credential-provider-env': 3.696.0 + '@aws-sdk/credential-provider-http': 3.696.0 + '@aws-sdk/credential-provider-process': 3.696.0 + '@aws-sdk/credential-provider-sso': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)) + '@aws-sdk/credential-provider-web-identity': 3.696.0(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/types': 3.696.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-node@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.696.0 + '@aws-sdk/credential-provider-http': 3.696.0 + '@aws-sdk/credential-provider-ini': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/credential-provider-process': 3.696.0 + '@aws-sdk/credential-provider-sso': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)) + '@aws-sdk/credential-provider-web-identity': 3.696.0(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/types': 3.696.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + + '@aws-sdk/credential-provider-process@3.696.0': + dependencies: + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-sso@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))': + dependencies: + '@aws-sdk/client-sso': 3.696.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/token-providers': 3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0)) + '@aws-sdk/types': 3.696.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.696.0(@aws-sdk/client-sts@3.699.0)': + dependencies: + '@aws-sdk/client-sts': 3.699.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-bucket-endpoint@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-config-provider': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-expect-continue@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-flexible-checksums@3.697.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/is-array-buffer': 3.0.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-host-header@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-location-constraint@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-logger@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-recursion-detection@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-sdk-s3@3.696.0': + dependencies: + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/core': 2.5.4 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-ssec@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/middleware-user-agent@3.696.0': + dependencies: + '@aws-sdk/core': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@aws-sdk/util-endpoints': 3.696.0 + '@smithy/core': 2.5.4 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/region-config-resolver@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.10 + tslib: 2.8.0 + + '@aws-sdk/signature-v4-multi-region@3.696.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/token-providers@3.699.0(@aws-sdk/client-sso-oidc@3.699.0(@aws-sdk/client-sts@3.699.0))': + dependencies: + '@aws-sdk/client-sso-oidc': 3.699.0(@aws-sdk/client-sts@3.699.0) + '@aws-sdk/types': 3.696.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/types@3.696.0': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/util-arn-parser@3.693.0': + dependencies: + tslib: 2.8.0 + + '@aws-sdk/util-endpoints@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/types': 3.7.1 + '@smithy/util-endpoints': 2.1.6 + tslib: 2.8.0 + + '@aws-sdk/util-locate-window@3.693.0': + dependencies: + tslib: 2.8.0 + + '@aws-sdk/util-user-agent-browser@3.696.0': + dependencies: + '@aws-sdk/types': 3.696.0 + '@smithy/types': 3.7.1 + bowser: 2.11.0 + tslib: 2.8.0 + + '@aws-sdk/util-user-agent-node@3.696.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.696.0 + '@aws-sdk/types': 3.696.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@aws-sdk/xml-builder@3.696.0': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -7236,6 +8122,337 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@smithy/abort-controller@3.1.8': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/chunked-blob-reader-native@3.0.1': + dependencies: + '@smithy/util-base64': 3.0.0 + tslib: 2.8.0 + + '@smithy/chunked-blob-reader@4.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/config-resolver@3.0.12': + dependencies: + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.10 + tslib: 2.8.0 + + '@smithy/core@2.5.4': + dependencies: + '@smithy/middleware-serde': 3.0.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/credential-provider-imds@3.2.7': + dependencies: + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + tslib: 2.8.0 + + '@smithy/eventstream-codec@3.1.9': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 3.7.1 + '@smithy/util-hex-encoding': 3.0.0 + tslib: 2.8.0 + + '@smithy/eventstream-serde-browser@3.0.13': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.12 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/eventstream-serde-config-resolver@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/eventstream-serde-node@3.0.12': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.12 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/eventstream-serde-universal@3.0.12': + dependencies: + '@smithy/eventstream-codec': 3.1.9 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/fetch-http-handler@4.1.1': + dependencies: + '@smithy/protocol-http': 4.1.7 + '@smithy/querystring-builder': 3.0.10 + '@smithy/types': 3.7.1 + '@smithy/util-base64': 3.0.0 + tslib: 2.8.0 + + '@smithy/hash-blob-browser@3.1.9': + dependencies: + '@smithy/chunked-blob-reader': 4.0.0 + '@smithy/chunked-blob-reader-native': 3.0.1 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/hash-node@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/hash-stream-node@3.1.9': + dependencies: + '@smithy/types': 3.7.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/invalid-dependency@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.0 + + '@smithy/is-array-buffer@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/md5-js@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/middleware-content-length@3.0.12': + dependencies: + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/middleware-endpoint@3.2.4': + dependencies: + '@smithy/core': 2.5.4 + '@smithy/middleware-serde': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-middleware': 3.0.10 + tslib: 2.8.0 + + '@smithy/middleware-retry@3.0.28': + dependencies: + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/service-error-classification': 3.0.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + tslib: 2.8.0 + uuid: 9.0.1 + + '@smithy/middleware-serde@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/middleware-stack@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/node-config-provider@3.1.11': + dependencies: + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/node-http-handler@3.3.1': + dependencies: + '@smithy/abort-controller': 3.1.8 + '@smithy/protocol-http': 4.1.7 + '@smithy/querystring-builder': 3.0.10 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/property-provider@3.1.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/protocol-http@4.1.7': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/querystring-builder@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.8.0 + + '@smithy/querystring-parser@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/service-error-classification@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + + '@smithy/shared-ini-file-loader@3.1.11': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/signature-v4@4.2.3': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-uri-escape': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/smithy-client@3.4.5': + dependencies: + '@smithy/core': 2.5.4 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-stack': 3.0.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-stream': 3.3.1 + tslib: 2.8.0 + + '@smithy/types@3.7.1': + dependencies: + tslib: 2.8.0 + + '@smithy/url-parser@3.0.10': + dependencies: + '@smithy/querystring-parser': 3.0.10 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/util-base64@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-body-length-browser@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-body-length-node@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.0 + + '@smithy/util-buffer-from@3.0.0': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-config-provider@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-defaults-mode-browser@3.0.28': + dependencies: + '@smithy/property-provider': 3.1.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + bowser: 2.11.0 + tslib: 2.8.0 + + '@smithy/util-defaults-mode-node@3.0.28': + dependencies: + '@smithy/config-resolver': 3.0.12 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/util-endpoints@2.1.6': + dependencies: + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/util-hex-encoding@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-middleware@3.0.10': + dependencies: + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/util-retry@3.0.10': + dependencies: + '@smithy/service-error-classification': 3.0.10 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + + '@smithy/util-stream@3.3.1': + dependencies: + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/node-http-handler': 3.3.1 + '@smithy/types': 3.7.1 + '@smithy/util-base64': 3.0.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-uri-escape@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.0 + + '@smithy/util-utf8@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-waiter@3.1.9': + dependencies: + '@smithy/abort-controller': 3.1.8 + '@smithy/types': 3.7.1 + tslib: 2.8.0 + '@socket.io/component-emitter@3.1.2': {} '@sqltools/formatter@1.2.5': {} @@ -8440,6 +9657,8 @@ snapshots: transitivePeerDependencies: - supports-color + bowser@2.11.0: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -9427,6 +10646,10 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-xml-parser@4.4.1: + dependencies: + strnum: 1.0.5 + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -11585,6 +12808,8 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@1.0.5: {} + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 From 094f2222d7888c18aa94df9c1c912e26a2a584c7 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 00:18:43 +0900 Subject: [PATCH 002/167] =?UTF-8?q?chore:=20=EC=95=88=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/media/src/ncp/ncp.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/media/src/ncp/ncp.service.ts b/apps/media/src/ncp/ncp.service.ts index 54dc4e8a..5e7dd559 100644 --- a/apps/media/src/ncp/ncp.service.ts +++ b/apps/media/src/ncp/ncp.service.ts @@ -10,7 +10,6 @@ import { NcpConfig } from './ncp.config'; @Injectable() export class NcpService { private s3: S3Client; - private endpoint: string; constructor( private ncpConfig: NcpConfig, From 644e41f2bebab6481c6eb978ee86d3e764dc2b50 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 02:01:55 +0900 Subject: [PATCH 003/167] =?UTF-8?q?feat:=20stream=20summary=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B5=AC=EC=A1=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/dto/createSummaryDto.ts | 4 +++ apps/api/src/stream/stream.controller.ts | 18 ++++++++-- apps/api/src/stream/stream.module.ts | 7 ++++ apps/api/src/stream/stream.service.ts | 39 +++++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 apps/api/src/stream/dto/createSummaryDto.ts create mode 100644 apps/api/src/stream/stream.service.ts diff --git a/apps/api/src/stream/dto/createSummaryDto.ts b/apps/api/src/stream/dto/createSummaryDto.ts new file mode 100644 index 00000000..a109014c --- /dev/null +++ b/apps/api/src/stream/dto/createSummaryDto.ts @@ -0,0 +1,4 @@ +export class CreateSummaryDto { + ticleId: number; + audioUrl: string; +} diff --git a/apps/api/src/stream/stream.controller.ts b/apps/api/src/stream/stream.controller.ts index 1b8b603b..8a8a2d20 100644 --- a/apps/api/src/stream/stream.controller.ts +++ b/apps/api/src/stream/stream.controller.ts @@ -1,4 +1,18 @@ -import { Controller } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; + +import { CreateSummaryDto } from './dto/createSummaryDto'; +import { StreamService } from './stream.service'; @Controller('stream') -export class StreamController {} +export class StreamController { + constructor(private readonly streamService: StreamService) {} + //업로드 완료된 오디오 파일의 s3주소를 미디어 서버에서 받아오기 + @Post('audio') + async getAudioFileUrl(@Body() createSummaryDto: CreateSummaryDto) { + const summary = await this.streamService.createSummary(createSummaryDto); + return summary; + } + + @Get('summary/:ticleId') + async getSummaryByTicleId(@Param('ticleId') ticleId: number) {} +} diff --git a/apps/api/src/stream/stream.module.ts b/apps/api/src/stream/stream.module.ts index e00607e6..d0418a0f 100644 --- a/apps/api/src/stream/stream.module.ts +++ b/apps/api/src/stream/stream.module.ts @@ -1,8 +1,15 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { Summary } from '@/entity/summary.entity'; +import { Ticle } from '@/entity/ticle.entity'; import { StreamController } from './stream.controller'; +import { StreamService } from './stream.service'; @Module({ + imports: [TypeOrmModule.forFeature([Ticle, Summary])], controllers: [StreamController], + providers: [StreamService], }) export class StreamModule {} diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts new file mode 100644 index 00000000..46f569bb --- /dev/null +++ b/apps/api/src/stream/stream.service.ts @@ -0,0 +1,39 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { ErrorMessage } from '@repo/types'; + +import { Summary } from '@/entity/summary.entity'; +import { Ticle } from '@/entity/ticle.entity'; + +import { CreateSummaryDto } from './dto/createSummaryDto'; + +@Injectable() +export class StreamService { + constructor( + @InjectRepository(Summary) + private summaryRepository: Repository, + @InjectRepository(Ticle) + private ticleRepository: Repository, + private configService: ConfigService + ) {} + + async createSummary(createSummaryDto: CreateSummaryDto) { + const { ticleId, audioUrl } = createSummaryDto; + const ticle = await this.ticleRepository.findOne({ + where: { id: ticleId }, + }); + + if (!ticle) { + throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); + } + + const summary = this.summaryRepository.create({ + name: ticle.title, + source: audioUrl, + ticle: ticle, + }); + return await this.summaryRepository.save(summary); + } +} From 7d490afbc35c1b7f8eeb445c47226bc019981994 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 02:31:45 +0900 Subject: [PATCH 004/167] =?UTF-8?q?feat:=20=EC=9D=8C=EC=84=B1=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=ED=85=8D=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/stream.service.ts | 45 +++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts index 46f569bb..09e569b7 100644 --- a/apps/api/src/stream/stream.service.ts +++ b/apps/api/src/stream/stream.service.ts @@ -7,8 +7,6 @@ import { ErrorMessage } from '@repo/types'; import { Summary } from '@/entity/summary.entity'; import { Ticle } from '@/entity/ticle.entity'; -import { CreateSummaryDto } from './dto/createSummaryDto'; - @Injectable() export class StreamService { constructor( @@ -19,8 +17,7 @@ export class StreamService { private configService: ConfigService ) {} - async createSummary(createSummaryDto: CreateSummaryDto) { - const { ticleId, audioUrl } = createSummaryDto; + async createSummary(ticleId: number, audioUrl: string, summaryText: string[]) { const ticle = await this.ticleRepository.findOne({ where: { id: ticleId }, }); @@ -30,10 +27,46 @@ export class StreamService { } const summary = this.summaryRepository.create({ - name: ticle.title, - source: audioUrl, + audioUrl: audioUrl, + summaryText: summaryText, ticle: ticle, }); return await this.summaryRepository.save(summary); } + + async transcribeAudio(remoteFileName: string) { + const speechEndpoint = this.configService.get('CLOVASPEECH_ENDPOINT'); + + const body = { + dataKey: remoteFileName, + params: { enable: true }, + language: 'ko-KR', + completion: 'sync', + fulltext: true, + }; + + try { + const response = await fetch(speechEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CLOVASPEECH-API-KEY': this.configService.get('CLOVASPEECH_API_KEY'), + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + const errorText = await response.text(); + console.error('Error response:', errorText); + throw new Error(`Failed to transcribe audio: ${response.status}`); + } + + const data = await response.json(); + + return data.text; + } catch (error) { + console.error('Failed to transcribe audio:', error); + throw new Error('Failed to transcribe audio with Clova Speech Recognition'); + } + } } From f0701dd0820520ba3a83cfc023c3474796aa1e94 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 02:32:31 +0900 Subject: [PATCH 005/167] =?UTF-8?q?feat:=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=9A=94=EC=95=BD=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/stream.service.ts | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts index 09e569b7..eb0084ca 100644 --- a/apps/api/src/stream/stream.service.ts +++ b/apps/api/src/stream/stream.service.ts @@ -69,4 +69,42 @@ export class StreamService { throw new Error('Failed to transcribe audio with Clova Speech Recognition'); } } + + async summaryAudio(text: string) { + const studioEndpoint = this.configService.get('CLOVASTUDIO_ENDPOINT'); + + const body = { + texts: [text], + segMinSize: 300, + includeAiFilters: true, + autoSentenceSplitter: true, + segCount: -1, + segMaxSize: 1000, + }; + + try { + const response = await fetch(studioEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-NCP-CLOVASTUDIO-API-KEY': this.configService.get('CLOVASTUDIO_API_KEY'), + 'X-NCP-APIGW-API-KEY': this.configService.get('CLOVASTUDIO_APIGW_API_KEY'), + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + const errorText = await response.text(); + console.error('Error response:', errorText); + throw new Error(`API error: ${response.status} - ${response.statusText}`); + } + + const data = await response.json(); + + return data.result.text; + } catch (error) { + console.error('Failed to summarize audio:', error); + throw new Error('Failed to summarize audio with Clova Studio'); + } + } } From 1d2c5930f76f6bd069063867a2d82544220552e6 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 02:33:24 +0900 Subject: [PATCH 006/167] =?UTF-8?q?feat:=20stream=20api=20=EC=9A=94?= =?UTF-8?q?=EC=95=BD=EA=B8=B0=EB=8A=A5,=20=EC=83=9D=EC=84=B1,=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/entity/summary.entity.ts | 9 +++------ apps/api/src/stream/stream.controller.ts | 14 +++++++++++--- apps/api/src/stream/stream.service.ts | 7 +++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/api/src/entity/summary.entity.ts b/apps/api/src/entity/summary.entity.ts index 0986cf52..b8a2b507 100644 --- a/apps/api/src/entity/summary.entity.ts +++ b/apps/api/src/entity/summary.entity.ts @@ -15,13 +15,10 @@ export class Summary { id: number; @Column('varchar') - name: string; + audioUrl: string; - @Column('varchar') - source: string; - - @Column('varchar') - summary: string; + @Column('json') + summaryText: string[]; @CreateDateColumn({ type: 'timestamp', name: 'created_at' }) createdAt: Date; diff --git a/apps/api/src/stream/stream.controller.ts b/apps/api/src/stream/stream.controller.ts index 8a8a2d20..a64df1ed 100644 --- a/apps/api/src/stream/stream.controller.ts +++ b/apps/api/src/stream/stream.controller.ts @@ -9,10 +9,18 @@ export class StreamController { //업로드 완료된 오디오 파일의 s3주소를 미디어 서버에서 받아오기 @Post('audio') async getAudioFileUrl(@Body() createSummaryDto: CreateSummaryDto) { - const summary = await this.streamService.createSummary(createSummaryDto); - return summary; + const fulltext = await this.streamService.transcribeAudio(createSummaryDto.audioUrl); + const summaryResult = await this.streamService.summaryAudio(fulltext); + await this.streamService.createSummary( + createSummaryDto.ticleId, + createSummaryDto.audioUrl, + summaryResult + ); } @Get('summary/:ticleId') - async getSummaryByTicleId(@Param('ticleId') ticleId: number) {} + async getSummaryByTicleId(@Param('ticleId') ticleId: number) { + const text = await this.streamService.getSummaryText(ticleId); + return { summary: text }; + } } diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts index eb0084ca..429eb615 100644 --- a/apps/api/src/stream/stream.service.ts +++ b/apps/api/src/stream/stream.service.ts @@ -107,4 +107,11 @@ export class StreamService { throw new Error('Failed to summarize audio with Clova Studio'); } } + + async getSummaryText(ticleId: number) { + const summary = await this.summaryRepository.findOne({ + where: { ticle: { id: ticleId } }, + }); + return summary.summaryText; + } } From d79d057efdde81ebe0733d486c6a495a0c815014 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 22:14:36 +0900 Subject: [PATCH 007/167] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/stream.service.ts | 13 +++---------- packages/types/src/errorMessages.ts | 2 ++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts index 429eb615..f1bea767 100644 --- a/apps/api/src/stream/stream.service.ts +++ b/apps/api/src/stream/stream.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; @@ -55,18 +55,11 @@ export class StreamService { body: JSON.stringify(body), }); - if (!response.ok) { - const errorText = await response.text(); - console.error('Error response:', errorText); - throw new Error(`Failed to transcribe audio: ${response.status}`); - } - const data = await response.json(); - return data.text; } catch (error) { console.error('Failed to transcribe audio:', error); - throw new Error('Failed to transcribe audio with Clova Speech Recognition'); + throw new InternalServerErrorException(ErrorMessage.FAILED_TO_TRANSCRIBE_AUDIO); } } @@ -104,7 +97,7 @@ export class StreamService { return data.result.text; } catch (error) { console.error('Failed to summarize audio:', error); - throw new Error('Failed to summarize audio with Clova Studio'); + throw new InternalServerErrorException(ErrorMessage.FAILED_TO_SUMMARY_AUDIO); } } diff --git a/packages/types/src/errorMessages.ts b/packages/types/src/errorMessages.ts index b4aa65cd..5ab3af1e 100644 --- a/packages/types/src/errorMessages.ts +++ b/packages/types/src/errorMessages.ts @@ -14,6 +14,8 @@ export const ErrorMessage = { FAILED_TO_CREATE_TICLE: '티클 생성에 실패했습니다', CANNOT_REQUEST_OWN_TICLE: '자신이 발표자인 티클에는 신청할 수 없습니다', TICLE_ALREADY_REQUESTED: '이미 신청한 티클입니다', + FAILED_TO_TRANSCRIBE_AUDIO: 'CLOVA SPEECH로 텍스트 변환에 실패했습니다', + FAILED_TO_SUMMARY_AUDIO: 'CLOVA STRUDIO로 요약에 실패했습니다', // socket error PEER_NOT_FOUND_IN_ROOM: '방에 해당 peer가 존재하지 않습니다', From e9a35fc873d6956cb906dec34b3f898e913228f4 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 22:27:06 +0900 Subject: [PATCH 008/167] =?UTF-8?q?feat:=20=EC=98=A4=EB=94=94=EC=98=A4=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/stream.controller.ts | 9 ++++----- apps/api/src/stream/stream.service.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/api/src/stream/stream.controller.ts b/apps/api/src/stream/stream.controller.ts index a64df1ed..37230536 100644 --- a/apps/api/src/stream/stream.controller.ts +++ b/apps/api/src/stream/stream.controller.ts @@ -9,13 +9,12 @@ export class StreamController { //업로드 완료된 오디오 파일의 s3주소를 미디어 서버에서 받아오기 @Post('audio') async getAudioFileUrl(@Body() createSummaryDto: CreateSummaryDto) { + const summary = await this.streamService.createSummary(createSummaryDto); + const fulltext = await this.streamService.transcribeAudio(createSummaryDto.audioUrl); const summaryResult = await this.streamService.summaryAudio(fulltext); - await this.streamService.createSummary( - createSummaryDto.ticleId, - createSummaryDto.audioUrl, - summaryResult - ); + + await this.streamService.updateSummaryText(summary, summaryResult); } @Get('summary/:ticleId') diff --git a/apps/api/src/stream/stream.service.ts b/apps/api/src/stream/stream.service.ts index f1bea767..77e1924f 100644 --- a/apps/api/src/stream/stream.service.ts +++ b/apps/api/src/stream/stream.service.ts @@ -7,6 +7,8 @@ import { ErrorMessage } from '@repo/types'; import { Summary } from '@/entity/summary.entity'; import { Ticle } from '@/entity/ticle.entity'; +import { CreateSummaryDto } from './dto/createSummaryDto'; + @Injectable() export class StreamService { constructor( @@ -17,7 +19,8 @@ export class StreamService { private configService: ConfigService ) {} - async createSummary(ticleId: number, audioUrl: string, summaryText: string[]) { + async createSummary(createSummaryDto: CreateSummaryDto) { + const { ticleId, audioUrl } = createSummaryDto; const ticle = await this.ticleRepository.findOne({ where: { id: ticleId }, }); @@ -28,7 +31,6 @@ export class StreamService { const summary = this.summaryRepository.create({ audioUrl: audioUrl, - summaryText: summaryText, ticle: ticle, }); return await this.summaryRepository.save(summary); @@ -107,4 +109,9 @@ export class StreamService { }); return summary.summaryText; } + + async updateSummaryText(summary: Summary, summaryText: string[]) { + summary.summaryText = summaryText; + await this.summaryRepository.save(summary); + } } From 37151991979a261a82bae94baa1aa6c0f5eb51c3 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Wed, 27 Nov 2024 22:27:51 +0900 Subject: [PATCH 009/167] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/stream/stream.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/stream/stream.controller.ts b/apps/api/src/stream/stream.controller.ts index 37230536..9d00f36f 100644 --- a/apps/api/src/stream/stream.controller.ts +++ b/apps/api/src/stream/stream.controller.ts @@ -6,7 +6,7 @@ import { StreamService } from './stream.service'; @Controller('stream') export class StreamController { constructor(private readonly streamService: StreamService) {} - //업로드 완료된 오디오 파일의 s3주소를 미디어 서버에서 받아오기 + @Post('audio') async getAudioFileUrl(@Body() createSummaryDto: CreateSummaryDto) { const summary = await this.streamService.createSummary(createSummaryDto); From 7be4131952c94ebc6f46a73101b548841e8391fa Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Thu, 28 Nov 2024 13:29:06 +0900 Subject: [PATCH 010/167] =?UTF-8?q?feat:=20TicleStatus=EC=97=90=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=EC=A4=91=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/src/ticle/ticleDetail.ts | 2 +- packages/types/src/ticle/ticleStatus.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/types/src/ticle/ticleDetail.ts b/packages/types/src/ticle/ticleDetail.ts index cb808b88..35c71fdc 100644 --- a/packages/types/src/ticle/ticleDetail.ts +++ b/packages/types/src/ticle/ticleDetail.ts @@ -12,7 +12,7 @@ export const TicleDetailResponseSchema = z.object({ content: z.string(), startTime: z.string().datetime(), endTime: z.string().datetime(), - ticleStatus: z.enum([TicleStatus.CLOSED, TicleStatus.OPEN]), + ticleStatus: z.enum([TicleStatus.CLOSED, TicleStatus.OPEN, TicleStatus.IN_PROGRESS]), createdAt: z.string().datetime(), tags: z.array(z.string()), speakerImgUrl: z.string().url(), diff --git a/packages/types/src/ticle/ticleStatus.ts b/packages/types/src/ticle/ticleStatus.ts index 86b298a5..57a00113 100644 --- a/packages/types/src/ticle/ticleStatus.ts +++ b/packages/types/src/ticle/ticleStatus.ts @@ -1,5 +1,6 @@ export const TicleStatus = { OPEN: 'open', + IN_PROGRESS: 'in_progress', CLOSED: 'closed', } as const; From 1e74c25a566b74d21fc7c4aad799f049705c5c88 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Thu, 28 Nov 2024 13:48:31 +0900 Subject: [PATCH 011/167] =?UTF-8?q?feat:=20ticle=20status=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/dashboard/dashboard.service.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/api/src/dashboard/dashboard.service.ts b/apps/api/src/dashboard/dashboard.service.ts index 5a36e659..161ff591 100644 --- a/apps/api/src/dashboard/dashboard.service.ts +++ b/apps/api/src/dashboard/dashboard.service.ts @@ -31,7 +31,13 @@ export class DashboardService { .take(pageSize); if (status) { - queryBuilder.andWhere('ticle.ticleStatus = :status', { status }); + if (status === TicleStatus.OPEN) { + queryBuilder.andWhere('ticle.ticleStatus IN (:...statuses)', { + statuses: [TicleStatus.OPEN, TicleStatus.IN_PROGRESS], + }); + } else { + queryBuilder.andWhere('ticle.ticleStatus = :status', { status }); + } } const [ticles, totalItems] = await queryBuilder.getManyAndCount(); @@ -70,7 +76,13 @@ export class DashboardService { .take(pageSize); if (status) { - queryBuilder.andWhere('ticle.ticleStatus = :status', { status }); + if (status === TicleStatus.OPEN) { + queryBuilder.andWhere('ticle.ticleStatus IN (:...statuses)', { + statuses: [TicleStatus.OPEN, TicleStatus.IN_PROGRESS], + }); + } else { + queryBuilder.andWhere('ticle.ticleStatus = :status', { status }); + } } const [applicants, totalItems] = await queryBuilder.getManyAndCount(); From 3f1b52f265be516f8ab6248bad27d21c98151a9f Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Thu, 28 Nov 2024 13:58:14 +0900 Subject: [PATCH 012/167] =?UTF-8?q?feat:=20ticle=20=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80,=20=EC=83=81=EC=84=B8=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=97=90=20status=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/ticle/ticle.service.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/api/src/ticle/ticle.service.ts b/apps/api/src/ticle/ticle.service.ts index 34f15d05..05877e5f 100644 --- a/apps/api/src/ticle/ticle.service.ts +++ b/apps/api/src/ticle/ticle.service.ts @@ -135,7 +135,15 @@ export class TicleService { .leftJoinAndSelect('ticle.speaker', 'speaker') .leftJoinAndSelect('ticle.applicants', 'applicants') .leftJoinAndSelect('applicants.user', 'user') - .select(['ticle', 'tags', 'speaker.id', 'speaker.profileImageUrl', 'applicants', 'user.id']) + .select([ + 'ticle', + 'tags', + 'speaker.id', + 'speaker.profileImageUrl', + 'applicants', + 'user.id', + 'ticle.ticleStatus', + ]) .where('ticle.id = :id', { id: ticleId }) .getOne(); @@ -176,8 +184,8 @@ export class TicleService { .leftJoin('ticle.tags', 'tags') .leftJoin('ticle.applicants', 'applicant') .leftJoin('ticle.speaker', 'speaker') - .where('ticle.ticleStatus = :status', { - status: isOpen ? TicleStatus.OPEN : TicleStatus.CLOSED, + .where('ticle.ticleStatus IN (:...statuses)', { + statuses: isOpen ? [TicleStatus.OPEN, TicleStatus.IN_PROGRESS] : [TicleStatus.CLOSED], }) .groupBy('ticle.id'); From e4391463c9f72897752f6559c2402d0b99550581 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Thu, 28 Nov 2024 14:08:15 +0900 Subject: [PATCH 013/167] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/src/dashboard/getDashboardList.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/types/src/dashboard/getDashboardList.ts b/packages/types/src/dashboard/getDashboardList.ts index 191a449b..90b10c67 100644 --- a/packages/types/src/dashboard/getDashboardList.ts +++ b/packages/types/src/dashboard/getDashboardList.ts @@ -14,7 +14,10 @@ export const GetDashboardListQuerySchema = z.object({ .optional() .transform((val) => (val ? parseInt(val, 10) : 10)) .refine((val) => !isNaN(val) && val > 0, '페이지 크기는 0보다 커야 합니다'), - status: z.enum([TicleStatus.CLOSED, TicleStatus.OPEN]).nullable().optional(), + status: z + .enum([TicleStatus.CLOSED, TicleStatus.OPEN, TicleStatus.IN_PROGRESS]) + .nullable() + .optional(), }); export type GetDashboardListQueryType = z.infer; @@ -32,7 +35,7 @@ const BaseDashboardResponseSchema = z.object({ title: z.string(), startTime: z.string().datetime(), endTime: z.string().datetime(), - ticleStatus: z.enum([TicleStatus.CLOSED, TicleStatus.OPEN]), + ticleStatus: z.enum([TicleStatus.CLOSED, TicleStatus.OPEN, TicleStatus.IN_PROGRESS]), }); const AppliedTicleSchema = BaseDashboardResponseSchema.extend({ From e7ce6d58a98f783becd3196881eed1531cd7520e Mon Sep 17 00:00:00 2001 From: seongha <11pi885@gmail.com> Date: Thu, 28 Nov 2024 14:26:52 +0900 Subject: [PATCH 014/167] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91=EC=8B=9C=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/src/dashboard/dashboard.controller.ts | 6 ++++- apps/api/src/dashboard/dashboard.service.ts | 24 +++++++++++++++++-- packages/types/src/errorMessages.ts | 1 + 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/api/src/dashboard/dashboard.controller.ts b/apps/api/src/dashboard/dashboard.controller.ts index 91c03aff..fb5fbd58 100644 --- a/apps/api/src/dashboard/dashboard.controller.ts +++ b/apps/api/src/dashboard/dashboard.controller.ts @@ -32,7 +32,11 @@ export class DashboardController { } @Post('start') - startTicle(@Param('ticleId') ticleId: number) {} + @UseGuards(JwtAuthGuard) + async startTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { + await this.dashboardService.startTicle(userId, ticleId); + return 'success ticle start'; + } @Post('join') joinTicle(@Param('ticleId') ticleId: number) {} diff --git a/apps/api/src/dashboard/dashboard.service.ts b/apps/api/src/dashboard/dashboard.service.ts index 161ff591..1ad61f66 100644 --- a/apps/api/src/dashboard/dashboard.service.ts +++ b/apps/api/src/dashboard/dashboard.service.ts @@ -1,7 +1,7 @@ -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { TicleStatus } from '@repo/types'; +import { ErrorMessage, TicleStatus } from '@repo/types'; import { Applicant } from '@/entity/applicant.entity'; import { Ticle } from '@/entity/ticle.entity'; @@ -114,4 +114,24 @@ export class DashboardService { }, }); } + + async startTicle(userId: number, ticleId: number) { + const ticle = await this.ticleRepository.findOne({ + where: { id: ticleId }, + }); + + if (!ticle) { + throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); + } + if (ticle.speaker.id !== userId) { + throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + } + if (ticle.ticleStatus !== TicleStatus.OPEN) { + throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + } + + ticle.ticleStatus = TicleStatus.IN_PROGRESS; + await this.ticleRepository.save(ticle); + return; + } } diff --git a/packages/types/src/errorMessages.ts b/packages/types/src/errorMessages.ts index 911793dc..38cd199d 100644 --- a/packages/types/src/errorMessages.ts +++ b/packages/types/src/errorMessages.ts @@ -15,6 +15,7 @@ export const ErrorMessage = { CANNOT_REQUEST_OWN_TICLE: '자신이 발표자인 티클에는 신청할 수 없습니다', TICLE_ALREADY_REQUESTED: '이미 신청한 티클입니다', CANNOT_DELETE_OTHERS_TICLE: '다른 사람의 티클은 삭제할 수 없습니다', + CANNOT_START_TICLE: '티클을 시작할 수 없습니다', // socket error PEER_NOT_FOUND_IN_ROOM: '방에 해당 peer가 존재하지 않습니다', From 641ad1e2ab79fc98dbb66d309f26597996d59d7b Mon Sep 17 00:00:00 2001 From: seongha <11pi885@gmail.com> Date: Thu, 28 Nov 2024 14:37:19 +0900 Subject: [PATCH 015/167] =?UTF-8?q?feat:=20ticle=20=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/src/dashboard/dashboard.controller.ts | 7 +++++++ apps/api/src/dashboard/dashboard.service.ts | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/apps/api/src/dashboard/dashboard.controller.ts b/apps/api/src/dashboard/dashboard.controller.ts index fb5fbd58..3d2782b3 100644 --- a/apps/api/src/dashboard/dashboard.controller.ts +++ b/apps/api/src/dashboard/dashboard.controller.ts @@ -38,6 +38,13 @@ export class DashboardController { return 'success ticle start'; } + @Post('end') + @UseGuards(JwtAuthGuard) + async endTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { + await this.dashboardService.endTicle(userId, ticleId); + return 'success ticle end'; + } + @Post('join') joinTicle(@Param('ticleId') ticleId: number) {} } diff --git a/apps/api/src/dashboard/dashboard.service.ts b/apps/api/src/dashboard/dashboard.service.ts index 1ad61f66..ae9ad806 100644 --- a/apps/api/src/dashboard/dashboard.service.ts +++ b/apps/api/src/dashboard/dashboard.service.ts @@ -134,4 +134,24 @@ export class DashboardService { await this.ticleRepository.save(ticle); return; } + + async endTicle(userId: number, ticleId: number) { + const ticle = await this.ticleRepository.findOne({ + where: { id: ticleId }, + }); + + if (!ticle) { + throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); + } + if (ticle.speaker.id !== userId) { + throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + } + if (ticle.ticleStatus !== TicleStatus.OPEN) { + throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + } + + ticle.ticleStatus = TicleStatus.CLOSED; + await this.ticleRepository.save(ticle); + return; + } } From 5be8d2a91978d2552e72e1e7703dcbeb138ac61d Mon Sep 17 00:00:00 2001 From: seongha <11pi885@gmail.com> Date: Thu, 28 Nov 2024 14:48:54 +0900 Subject: [PATCH 016/167] =?UTF-8?q?fix:=20=ED=8B=B0=ED=81=B4=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91&=EC=A2=85=EB=A3=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/dashboard/dashboard.controller.ts | 4 ++-- apps/api/src/dashboard/dashboard.service.ts | 8 +++++--- packages/types/src/errorMessages.ts | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/api/src/dashboard/dashboard.controller.ts b/apps/api/src/dashboard/dashboard.controller.ts index 3d2782b3..fbd84d9a 100644 --- a/apps/api/src/dashboard/dashboard.controller.ts +++ b/apps/api/src/dashboard/dashboard.controller.ts @@ -31,14 +31,14 @@ export class DashboardController { return await this.dashboardService.getApplicants(ticleId); } - @Post('start') + @Post(':ticleId/start') @UseGuards(JwtAuthGuard) async startTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { await this.dashboardService.startTicle(userId, ticleId); return 'success ticle start'; } - @Post('end') + @Post(':ticleId/end') @UseGuards(JwtAuthGuard) async endTicle(@GetUserId() userId: number, @Param('ticleId') ticleId: number) { await this.dashboardService.endTicle(userId, ticleId); diff --git a/apps/api/src/dashboard/dashboard.service.ts b/apps/api/src/dashboard/dashboard.service.ts index ae9ad806..78b89263 100644 --- a/apps/api/src/dashboard/dashboard.service.ts +++ b/apps/api/src/dashboard/dashboard.service.ts @@ -118,6 +118,7 @@ export class DashboardService { async startTicle(userId: number, ticleId: number) { const ticle = await this.ticleRepository.findOne({ where: { id: ticleId }, + relations: ['speaker'], }); if (!ticle) { @@ -138,16 +139,17 @@ export class DashboardService { async endTicle(userId: number, ticleId: number) { const ticle = await this.ticleRepository.findOne({ where: { id: ticleId }, + relations: ['speaker'], }); if (!ticle) { throw new NotFoundException(ErrorMessage.TICLE_NOT_FOUND); } if (ticle.speaker.id !== userId) { - throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + throw new BadRequestException(ErrorMessage.CANNOT_END_TICLE); } - if (ticle.ticleStatus !== TicleStatus.OPEN) { - throw new BadRequestException(ErrorMessage.CANNOT_START_TICLE); + if (ticle.ticleStatus !== TicleStatus.IN_PROGRESS) { + throw new BadRequestException(ErrorMessage.CANNOT_END_TICLE); } ticle.ticleStatus = TicleStatus.CLOSED; diff --git a/packages/types/src/errorMessages.ts b/packages/types/src/errorMessages.ts index 38cd199d..c4097ccc 100644 --- a/packages/types/src/errorMessages.ts +++ b/packages/types/src/errorMessages.ts @@ -16,6 +16,7 @@ export const ErrorMessage = { TICLE_ALREADY_REQUESTED: '이미 신청한 티클입니다', CANNOT_DELETE_OTHERS_TICLE: '다른 사람의 티클은 삭제할 수 없습니다', CANNOT_START_TICLE: '티클을 시작할 수 없습니다', + CANNOT_END_TICLE: '티클을 종료할 수 없습니다', // socket error PEER_NOT_FOUND_IN_ROOM: '방에 해당 peer가 존재하지 않습니다', From 9dafeeef43cdd243fc918ec1fb3e88a72d780572 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:44:18 +0900 Subject: [PATCH 017/167] =?UTF-8?q?docs:=20readme=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6a92f51a..757e19e3 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,18 @@ 메인 배너
-### [:ledger: 팀 노션](https://www.notion.so/simeunseo/9-Ticle-12e599a6f0d2804682ccd2251248a435?pvs=4) | [:mag: 위키](https://github.com/boostcampwm-2024/web21-boostproject/wiki) | [🎨 피그마](https://www.figma.com/design/nw74detTvjXGrDP2cfdmwp/TICLE-%EB%94%94%EC%9E%90%EC%9D%B8?node-id=32-4477&t=3FCCnBpgQXMZs63X-1) | [🗓️ 스프린트 백로그](https://github.com/orgs/boostcampwm-2024/projects/82/views/7) | [🧪 스토리북](https://673a0cccd15a760db778c591-kxdpixadfg.chromatic.com/?path=/docs/common-dialog--docs) +### [:ledger: 팀 노션](https://www.notion.so/simeunseo/9-Ticle-12e599a6f0d2804682ccd2251248a435?pvs=4) | [:mag: 위키](https://github.com/boostcampwm-2024/web21-boostproject/wiki) | [🎨 피그마](https://www.figma.com/design/nw74detTvjXGrDP2cfdmwp/TICLE-%EB%94%94%EC%9E%90%EC%9D%B8?node-id=32-4477&t=3FCCnBpgQXMZs63X-1) | [🗓️ 스프린트 백로그](https://github.com/orgs/boostcampwm-2024/projects/82/views/7) | [🧪 스토리북](https://673a0cccd15a760db778c591-ttyfhdnavn.chromatic.com/) # 🖧 시스템 아키텍처 -아키텍처 +![Cloudcraft Image (4)](https://github.com/user-attachments/assets/1e5874ee-2485-4e89-90a0-cebb47621c77) # 🏃 작업 진행 상황 -- [🆕 4주차 발표자료](https://simeunseo.notion.site/4-1ccf63bab4b14fd8b249f5d7c7cd7e53?pvs=4) +- [🆕 5주차 발표자료](https://www.figma.com/slides/kNBDNutRalcIkuGWo3LHd9/5%EC%A3%BC%EC%B0%A8-%EB%8D%B0%EB%AA%A8-%EB%B0%9C%ED%91%9C%EC%9E%90%EB%A3%8C?node-id=1-23&t=LH1JpAKm2xhJUq9q-1) +- [4주차 발표자료](https://simeunseo.notion.site/4-1ccf63bab4b14fd8b249f5d7c7cd7e53?pvs=4) - [3주차 발표자료](https://simeunseo.notion.site/3-0df689ca7cd3407b89a93284854a54b8?pvs=4) - [2주차 발표자료](https://simeunseo.notion.site/2-137599a6f0d2809fa498fa1cc31d97f9?pvs=4) - [1주차 발표자료](https://simeunseo.notion.site/1-130599a6f0d2804597e0c55e8ee33920?pvs=4) @@ -28,29 +29,33 @@ 카메라와 음성을 통해 발표자와 참여자가 실시간으로 지식을 공유할 수 있습니다. 화면 공유 기능으로 더욱 효과적인 지식 전달이 가능합니다. -화면공유 +![티클 시작](https://github.com/user-attachments/assets/f0c3f9b3-cbda-49b2-a6a3-4c77fd569129) -### **✔️ 티클 신청 시스템** +### **티클 목록** - 참여자는 티클 리스트에서 원하는 티클을 선택하고 신청할 수 있습니다. - 발표자는 자신의 티클 관리 페이지에서 신청자를 확인하고 발표를 시작할 수 있습니다. + 티클 목록을 확인하고 정렬, 필터링할 수 있습니다. -티클 리스트(메인) -
-개설한 티클 관리 -신청한 티클 관리 -
+![티클목록4](https://github.com/user-attachments/assets/ac4cbba2-5552-43d3-8361-4159f9c3a48b) + +### **✔️ 티클 개설** + + 발표자는 원하는 티클을 개설할 수 있습니다. + +![티클개설4](https://github.com/user-attachments/assets/56b33a06-a85d-49a2-b830-c46d44879ce2) -### **✔️ 티클 소개 페이지** +### **✔️ 티클 관리 대시보드** - 티클 내용과 발표자 정보를 제공합니다. + 참여자는 신청한 티클을 대시보드에서 확인하고 참가할 수 있습니다. + 발표자는 대시보드에서 신청자를 확인하고 발표를 시작할 수 있습니다. -티클 개설하기 +![대시보드4](https://github.com/user-attachments/assets/d6f57d72-135e-4e36-bba9-784a1fd368a2) ### **✔️ AI 요약 기능** CLOVA API를 이용해 티클 내용을 요약하여 제공합니다. +![image](https://github.com/user-attachments/assets/825a5ea0-e873-40e1-a866-5706bde4bf5a) + # ✍️ 학습 정리 | 분야 | 기술 | @@ -99,7 +104,7 @@ Web FE·BE - Web FE·BE + Web FE Web FE Web BE Web BE From 3e5bd80fa725b84061a8abc28f6838e6b77d4b8b Mon Sep 17 00:00:00 2001 From: seongha <11pi885@gmail.com> Date: Sat, 30 Nov 2024 13:04:08 +0900 Subject: [PATCH 018/167] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/{ => common/Interceptor}/response.interceptor.ts | 0 apps/api/src/{ => common/Interceptor}/response.interface.ts | 0 apps/api/src/common/filter/http-exception.filter.ts | 2 +- apps/api/src/{ => common/pipe}/zodValidationPipe.ts | 0 apps/api/src/dashboard/dashboard.controller.ts | 2 +- apps/api/src/main.ts | 2 +- apps/api/src/ticle/ticle.controller.ts | 2 +- 7 files changed, 4 insertions(+), 4 deletions(-) rename apps/api/src/{ => common/Interceptor}/response.interceptor.ts (100%) rename apps/api/src/{ => common/Interceptor}/response.interface.ts (100%) rename apps/api/src/{ => common/pipe}/zodValidationPipe.ts (100%) diff --git a/apps/api/src/response.interceptor.ts b/apps/api/src/common/Interceptor/response.interceptor.ts similarity index 100% rename from apps/api/src/response.interceptor.ts rename to apps/api/src/common/Interceptor/response.interceptor.ts diff --git a/apps/api/src/response.interface.ts b/apps/api/src/common/Interceptor/response.interface.ts similarity index 100% rename from apps/api/src/response.interface.ts rename to apps/api/src/common/Interceptor/response.interface.ts diff --git a/apps/api/src/common/filter/http-exception.filter.ts b/apps/api/src/common/filter/http-exception.filter.ts index a7c8ead8..eac4777a 100644 --- a/apps/api/src/common/filter/http-exception.filter.ts +++ b/apps/api/src/common/filter/http-exception.filter.ts @@ -1,7 +1,7 @@ import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; import { Response } from 'express'; -import { ErrorResponse } from '@/response.interface'; +import { ErrorResponse } from '@/common/Interceptor/response.interface'; @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { diff --git a/apps/api/src/zodValidationPipe.ts b/apps/api/src/common/pipe/zodValidationPipe.ts similarity index 100% rename from apps/api/src/zodValidationPipe.ts rename to apps/api/src/common/pipe/zodValidationPipe.ts diff --git a/apps/api/src/dashboard/dashboard.controller.ts b/apps/api/src/dashboard/dashboard.controller.ts index 91c03aff..d33fd1c2 100644 --- a/apps/api/src/dashboard/dashboard.controller.ts +++ b/apps/api/src/dashboard/dashboard.controller.ts @@ -3,7 +3,7 @@ import { GetDashboardListQuerySchema } from '@repo/types'; import { JwtAuthGuard } from '@/auth/jwt/jwt-auth.guard'; import { GetUserId } from '@/common/decorator/get-userId.decorator'; -import { ZodValidationPipe } from '@/zodValidationPipe'; +import { ZodValidationPipe } from '@/common/pipe/zodValidationPipe'; import { DashboardService } from './dashboard.service'; import { GetDashboardListQueryDto } from './dto/getDashboardListQueryDto'; diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 7f98c78e..d6e80e02 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -8,7 +8,7 @@ import { AppModule } from '@/app.module'; import { DBExceptionFilter } from './common/filter/db-exception.filter'; import { HttpExceptionFilter } from './common/filter/http-exception.filter'; -import { ResponseInterceptor } from './response.interceptor'; +import { ResponseInterceptor } from './common/Interceptor/response.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/apps/api/src/ticle/ticle.controller.ts b/apps/api/src/ticle/ticle.controller.ts index 4c5312d7..cbcdfafc 100644 --- a/apps/api/src/ticle/ticle.controller.ts +++ b/apps/api/src/ticle/ticle.controller.ts @@ -3,7 +3,7 @@ import { CreateTicleSchema } from '@repo/types'; import { JwtAuthGuard } from '@/auth/jwt/jwt-auth.guard'; import { GetUserId } from '@/common/decorator/get-userId.decorator'; -import { ZodValidationPipe } from '@/zodValidationPipe'; +import { ZodValidationPipe } from '@/common/pipe/zodValidationPipe'; import { CreateTicleDto } from './dto/createTicleDto'; import { GetTicleListQueryDto } from './dto/getTicleListQueryDto'; From 33d6fd116f14fc5604a9ce4545dd291a3e1a2784 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Sat, 30 Nov 2024 15:27:50 +0900 Subject: [PATCH 019/167] =?UTF-8?q?feat:=20=EC=9D=B8=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/src/ticle/ticleStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/ticle/ticleStatus.ts b/packages/types/src/ticle/ticleStatus.ts index 57a00113..a7456edb 100644 --- a/packages/types/src/ticle/ticleStatus.ts +++ b/packages/types/src/ticle/ticleStatus.ts @@ -1,6 +1,6 @@ export const TicleStatus = { OPEN: 'open', - IN_PROGRESS: 'in_progress', + IN_PROGRESS: 'inProgress', CLOSED: 'closed', } as const; From e85a5978e8d56d02cc660f003ebd602bff130642 Mon Sep 17 00:00:00 2001 From: LEE JIEUN Date: Sat, 30 Nov 2024 16:05:50 +0900 Subject: [PATCH 020/167] fix: createticle userid validate --- apps/api/src/ticle/ticle.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/ticle/ticle.service.ts b/apps/api/src/ticle/ticle.service.ts index 05877e5f..eef85028 100644 --- a/apps/api/src/ticle/ticle.service.ts +++ b/apps/api/src/ticle/ticle.service.ts @@ -27,7 +27,7 @@ export class TicleService { ) {} async createTicle(createTicleDto: CreateTicleDto, userId: number): Promise { - const user = await this.userRepository.findOne({ where: { id: userId } }); + const user = await this.getUserById(userId); const { existingTags, tagsToCreate } = await this.findExistingTags(createTicleDto.tags); const newTags = await this.createNewTags(tagsToCreate); From 785a19f9a12f25a36764858d65c55df93dd61a6a Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:54:00 +0900 Subject: [PATCH 021/167] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=EC=97=90=20'inProgress'=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/components/dashboard/apply/TicleInfoCard.tsx | 5 ++--- apps/web/src/components/dashboard/open/TicleInfoCard.tsx | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx b/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx index b87ea007..fd1bcb68 100644 --- a/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx @@ -10,7 +10,7 @@ interface TicleInfoCardProps { ticleTitle: string; startTime: string; endTime: string; - status: 'closed' | 'open'; + status: 'closed' | 'open' | 'inProgress'; } function TicleInfoCard({ @@ -48,9 +48,8 @@ function TicleInfoCard({ {`${dateStr} ${timeRangeStr}`} - diff --git a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx index 336d753c..d1df41d1 100644 --- a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx @@ -14,7 +14,7 @@ interface TicleInfoCardProps { ticleTitle: string; startTime: string; endTime: string; - status: 'closed' | 'open'; + status: 'closed' | 'open' | 'inProgress'; } function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: TicleInfoCardProps) { @@ -64,7 +64,7 @@ function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: Ticl {isOpen && ( From 50e8a51ef6f5df4f7175ce935b75545583c79ad3 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:12:15 +0900 Subject: [PATCH 022/167] =?UTF-8?q?refactor:=20=ED=8B=B0=ED=81=B4=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=A1=B0=EA=B1=B4=EB=B6=80=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/ticle/dto/ticleDetailDto.ts | 7 ++ .../src/components/ticle/detail/CtaButton.tsx | 77 +++++++++++++++++++ .../web/src/components/ticle/detail/index.tsx | 23 +++--- 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 apps/web/src/components/ticle/detail/CtaButton.tsx diff --git a/apps/api/src/ticle/dto/ticleDetailDto.ts b/apps/api/src/ticle/dto/ticleDetailDto.ts index c80ef6a3..f9ae1e46 100644 --- a/apps/api/src/ticle/dto/ticleDetailDto.ts +++ b/apps/api/src/ticle/dto/ticleDetailDto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { TicleStatus } from '@repo/types'; export class TickleDetailResponseDto { @ApiProperty({ @@ -74,4 +75,10 @@ export class TickleDetailResponseDto { description: '이미 참가신청을 했는지 여부', }) alreadyApplied: boolean; + + @ApiProperty({ + example: 'inProgress', + description: '티클의 상태', + }) + ticleStatus: TicleStatus; } diff --git a/apps/web/src/components/ticle/detail/CtaButton.tsx b/apps/web/src/components/ticle/detail/CtaButton.tsx new file mode 100644 index 00000000..d3a84c97 --- /dev/null +++ b/apps/web/src/components/ticle/detail/CtaButton.tsx @@ -0,0 +1,77 @@ +import { ReactNode } from 'react'; +import { TicleStatus } from '@repo/types'; + +import TrashIc from '@/assets/icons/trash.svg?react'; +import Button from '@/components/common/Button'; + +type ButtonState = 'owner' | 'applied' | 'closed' | 'default'; + +interface ButtonConfig { + onClick: (() => void) | undefined; + disabled: boolean; + content: ReactNode; +} + +type ButtonConfigMap = Record; + +interface CtaButtonProps { + isOwner: boolean; + alreadyApplied: boolean; + ticleStatus: TicleStatus; + onDelete: () => void; + onApply: () => void; +} + +function CtaButton({ isOwner, alreadyApplied, ticleStatus, onDelete, onApply }: CtaButtonProps) { + const getButtonState = (): ButtonState => { + if (isOwner) { + return 'owner'; + } + if (alreadyApplied) { + return 'applied'; + } + if (ticleStatus === 'closed') { + return 'closed'; + } + return 'default'; + }; + + const buttonConfig: ButtonConfigMap = { + owner: { + onClick: onDelete, + disabled: false, + content: ( + + 티클 삭제하기 + + + ), + }, + applied: { + onClick: undefined, + disabled: true, + content: '신청 완료', + }, + closed: { + onClick: undefined, + disabled: true, + content: '종료된 티클', + }, + default: { + onClick: onApply, + disabled: false, + content: '티클 신청하기', + }, + }; + + const buttonState = getButtonState(); + const config = buttonConfig[buttonState]; + + return ( + + ); +} + +export default CtaButton; diff --git a/apps/web/src/components/ticle/detail/index.tsx b/apps/web/src/components/ticle/detail/index.tsx index b23f7bee..265eb293 100644 --- a/apps/web/src/components/ticle/detail/index.tsx +++ b/apps/web/src/components/ticle/detail/index.tsx @@ -2,15 +2,15 @@ import { useParams } from '@tanstack/react-router'; import CalendarIc from '@/assets/icons/calendar.svg?react'; import ClockIc from '@/assets/icons/clock.svg?react'; -import TrashIc from '@/assets/icons/trash.svg?react'; import Avatar from '@/components/common/Avatar'; import Badge from '@/components/common/Badge'; -import Button from '@/components/common/Button'; import UserProfileDialog from '@/components/user/UserProfileDialog'; import { useApplyTicle, useDeleteTicle, useTicle } from '@/hooks/api/ticle'; import useModal from '@/hooks/useModal'; import { formatDateTimeRange } from '@/utils/date'; +import CtaButton from './CtaButton'; + function Detail() { const { ticleId } = useParams({ from: '/_authenticated/ticle/$ticleId' }); const { data } = useTicle(ticleId); @@ -84,18 +84,13 @@ function Detail() { - {data.isOwner ? ( - - ) : ( - - )} + ); } From 3b22b0f093f983e56d1d29d1caaaaafc3d6e442e Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:16:10 +0900 Subject: [PATCH 023/167] =?UTF-8?q?feat:=20getTicleList=20service=EC=97=90?= =?UTF-8?q?=EC=84=9C=20isOpen=20=ED=95=84=ED=84=B0=EB=A7=81=EC=8B=9C=20inp?= =?UTF-8?q?rogress=20=EC=83=81=ED=83=9C=EC=9D=B8=20=EA=B2=83=EB=8F=84=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/ticle/ticle.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/ticle/ticle.service.ts b/apps/api/src/ticle/ticle.service.ts index eef85028..26056c86 100644 --- a/apps/api/src/ticle/ticle.service.ts +++ b/apps/api/src/ticle/ticle.service.ts @@ -206,7 +206,7 @@ export class TicleService { .createQueryBuilder('ticle') .select('COUNT(*) as count') .where('ticle.ticleStatus = :status', { - status: isOpen ? TicleStatus.OPEN : TicleStatus.CLOSED, + status: isOpen ? TicleStatus.OPEN || TicleStatus.IN_PROGRESS : TicleStatus.CLOSED, }); const totalTicleCount = await countQuery.getRawOne(); From ef8aeb6000e66ce1e3f5dff8918b15a31c2469fd Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:21:30 +0900 Subject: [PATCH 024/167] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/api/dashboard.ts | 2 +- apps/web/src/components/dashboard/open/TicleInfoCard.tsx | 8 +++++--- apps/web/src/hooks/api/dashboard.ts | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/web/src/api/dashboard.ts b/apps/web/src/api/dashboard.ts index 502c7b72..17aeca4d 100644 --- a/apps/web/src/api/dashboard.ts +++ b/apps/web/src/api/dashboard.ts @@ -27,7 +27,7 @@ const getApplicantsTicle = async (ticleId: string) => { }; const startTicle = async (ticleId: string) => { - const { data } = await axiosInstance.post('/dashboard/start', { ticleId }); + const { data } = await axiosInstance.post(`/dashboard/${ticleId}/start`); return data; }; diff --git a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx index d1df41d1..4d0e49d9 100644 --- a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx @@ -3,7 +3,7 @@ import { MouseEvent } from 'react'; import PersonFilledIc from '@/assets/icons/person-filled.svg?react'; import Button from '@/components/common/Button'; -import { useApplicantsTicle } from '@/hooks/api/dashboard'; +import { useApplicantsTicle, useStartTicle } from '@/hooks/api/dashboard'; import useModal from '@/hooks/useModal'; import { formatDateTimeRange } from '@/utils/date'; @@ -21,14 +21,16 @@ function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: Ticl const { isOpen, onOpen, onClose } = useModal(); const { data: applicantsData } = useApplicantsTicle(ticleId.toString()); + const { mutate: ticleStartMutate } = useStartTicle(); const { dateStr, timeRangeStr } = formatDateTimeRange(startTime, endTime); const navigate = useNavigate(); if (!applicantsData) return; - const handleTicleParticipate = (e: MouseEvent) => { + const handleTicleStart = (e: MouseEvent) => { e.preventDefault(); + ticleStartMutate(ticleId.toString()); navigate({ to: `/live/${ticleId}` }); }; @@ -63,7 +65,7 @@ function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: Ticl - diff --git a/apps/web/src/hooks/api/dashboard.ts b/apps/web/src/hooks/api/dashboard.ts index 9ecfdd23..7cccd1b2 100644 --- a/apps/web/src/hooks/api/dashboard.ts +++ b/apps/web/src/hooks/api/dashboard.ts @@ -33,9 +33,8 @@ export const useStartTicle = () => { return useMutation({ mutationFn: startTicle, - onSuccess: (_, ticleId) => { + onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['appliedTicleList'] }); - queryClient.invalidateQueries({ queryKey: ['applicantsTicle', ticleId] }); }, }); }; From de6030148175bd1dc8b77a9e4e38cbd18432ec98 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:25:02 +0900 Subject: [PATCH 025/167] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20=EC=8B=9C=EC=9E=91=EB=90=9C=20?= =?UTF-8?q?=ED=8B=B0=ED=81=B4=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/dashboard/open/TicleInfoCard.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx index 4d0e49d9..3cd8364a 100644 --- a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx @@ -65,8 +65,15 @@ function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: Ticl - {isOpen && ( From 9ae164cc9f418225067f2471ce524818c5d840c3 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:33:44 +0900 Subject: [PATCH 026/167] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EB=82=98?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=EC=8B=9C=20isOwner=20=EC=97=AC=EB=B6=80?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=AA=A8=EB=8B=AC=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/components/live/ControlBar/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/live/ControlBar/index.tsx b/apps/web/src/components/live/ControlBar/index.tsx index c7c1dcd2..ddbcbb16 100644 --- a/apps/web/src/components/live/ControlBar/index.tsx +++ b/apps/web/src/components/live/ControlBar/index.tsx @@ -1,3 +1,4 @@ +import { useParams } from '@tanstack/react-router'; import { SOCKET_EVENTS } from '@repo/mediasoup'; import CameraOffIc from '@/assets/icons/camera-off.svg?react'; @@ -11,6 +12,7 @@ import ToggleButton from '@/components/live/ControlBar/ToggleButton'; import ExitDialog from '@/components/live/ExitDialog'; import { useLocalStreamAction, useLocalStreamState } from '@/contexts/localStream/context'; import { useMediasoupAction, useMediasoupState } from '@/contexts/mediasoup/context'; +import { useTicle } from '@/hooks/api/ticle'; import useModal from '@/hooks/useModal'; const ControlBar = () => { @@ -23,6 +25,10 @@ const ControlBar = () => { const { closeStream, pauseStream, resumeStream, startScreenStream, closeScreenStream } = useLocalStreamAction(); + const { ticleId } = useParams({ from: '/_authenticated/live/$ticleId' }); + const { data: ticleData } = useTicle(ticleId); + const isOwner = ticleData?.isOwner || false; + const toggleScreenShare = async () => { const { paused, stream } = screen; @@ -61,7 +67,7 @@ const ControlBar = () => { } }; - const handleExit = (isOwner: boolean) => { + const handleExit = () => { if (isOwner) { socketRef.current?.emit(SOCKET_EVENTS.closeRoom); } @@ -93,7 +99,7 @@ const ControlBar = () => { {isOpen && ( - + )} ); From 3c2c01339a08dc4c946f665f2de2196522d1a847 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:37:36 +0900 Subject: [PATCH 027/167] =?UTF-8?q?feat:=20=ED=8B=B0=ED=81=B4=20=EC=A2=85?= =?UTF-8?q?=EB=A3=8C=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/api/live.ts | 7 +++++++ apps/web/src/components/live/ControlBar/index.tsx | 4 ++++ apps/web/src/hooks/api/live.ts | 14 ++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 apps/web/src/api/live.ts create mode 100644 apps/web/src/hooks/api/live.ts diff --git a/apps/web/src/api/live.ts b/apps/web/src/api/live.ts new file mode 100644 index 00000000..a587745c --- /dev/null +++ b/apps/web/src/api/live.ts @@ -0,0 +1,7 @@ +import axiosInstance from './axios'; + +export const endTicle = async (ticleId: string) => { + const { data } = await axiosInstance.post(`/dashboard/${ticleId}/end`); + + return data; +}; diff --git a/apps/web/src/components/live/ControlBar/index.tsx b/apps/web/src/components/live/ControlBar/index.tsx index ddbcbb16..a44288ff 100644 --- a/apps/web/src/components/live/ControlBar/index.tsx +++ b/apps/web/src/components/live/ControlBar/index.tsx @@ -12,6 +12,7 @@ import ToggleButton from '@/components/live/ControlBar/ToggleButton'; import ExitDialog from '@/components/live/ExitDialog'; import { useLocalStreamAction, useLocalStreamState } from '@/contexts/localStream/context'; import { useMediasoupAction, useMediasoupState } from '@/contexts/mediasoup/context'; +import { useEndTicle } from '@/hooks/api/live'; import { useTicle } from '@/hooks/api/ticle'; import useModal from '@/hooks/useModal'; @@ -26,7 +27,9 @@ const ControlBar = () => { useLocalStreamAction(); const { ticleId } = useParams({ from: '/_authenticated/live/$ticleId' }); + const { data: ticleData } = useTicle(ticleId); + const { mutate: endTicleMutate } = useEndTicle(); const isOwner = ticleData?.isOwner || false; const toggleScreenShare = async () => { @@ -70,6 +73,7 @@ const ControlBar = () => { const handleExit = () => { if (isOwner) { socketRef.current?.emit(SOCKET_EVENTS.closeRoom); + endTicleMutate(ticleId); } disconnect(); diff --git a/apps/web/src/hooks/api/live.ts b/apps/web/src/hooks/api/live.ts new file mode 100644 index 00000000..8eb36356 --- /dev/null +++ b/apps/web/src/hooks/api/live.ts @@ -0,0 +1,14 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { endTicle } from '@/api/live'; + +export const useEndTicle = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: endTicle, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['appliedTicleList'] }); + }, + }); +}; From cba4fb29d58ebdf4dad1e7e95e47542b73ce26c7 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:47:20 +0900 Subject: [PATCH 028/167] =?UTF-8?q?feat:=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=ED=8B=B0=ED=81=B4=20=EC=A0=91=EC=86=8D=EC=8B=9C=20/=EB=A1=9C?= =?UTF-8?q?=20navigate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/live/ControlBar/index.tsx | 18 ++++++------- apps/web/src/components/live/index.tsx | 25 ++++++++++++++++++- apps/web/src/hooks/api/live.ts | 4 ++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/apps/web/src/components/live/ControlBar/index.tsx b/apps/web/src/components/live/ControlBar/index.tsx index a44288ff..9ca5440d 100644 --- a/apps/web/src/components/live/ControlBar/index.tsx +++ b/apps/web/src/components/live/ControlBar/index.tsx @@ -1,4 +1,3 @@ -import { useParams } from '@tanstack/react-router'; import { SOCKET_EVENTS } from '@repo/mediasoup'; import CameraOffIc from '@/assets/icons/camera-off.svg?react'; @@ -12,11 +11,14 @@ import ToggleButton from '@/components/live/ControlBar/ToggleButton'; import ExitDialog from '@/components/live/ExitDialog'; import { useLocalStreamAction, useLocalStreamState } from '@/contexts/localStream/context'; import { useMediasoupAction, useMediasoupState } from '@/contexts/mediasoup/context'; -import { useEndTicle } from '@/hooks/api/live'; -import { useTicle } from '@/hooks/api/ticle'; import useModal from '@/hooks/useModal'; -const ControlBar = () => { +interface ControlBarProps { + isOwner: boolean; + onTicleEnd: () => void; +} + +const ControlBar = ({ isOwner, onTicleEnd }: ControlBarProps) => { const { isOpen, onClose, onOpen } = useModal(); const { socketRef } = useMediasoupState(); @@ -26,12 +28,6 @@ const ControlBar = () => { const { closeStream, pauseStream, resumeStream, startScreenStream, closeScreenStream } = useLocalStreamAction(); - const { ticleId } = useParams({ from: '/_authenticated/live/$ticleId' }); - - const { data: ticleData } = useTicle(ticleId); - const { mutate: endTicleMutate } = useEndTicle(); - const isOwner = ticleData?.isOwner || false; - const toggleScreenShare = async () => { const { paused, stream } = screen; @@ -73,7 +69,7 @@ const ControlBar = () => { const handleExit = () => { if (isOwner) { socketRef.current?.emit(SOCKET_EVENTS.closeRoom); - endTicleMutate(ticleId); + onTicleEnd(); } disconnect(); diff --git a/apps/web/src/components/live/index.tsx b/apps/web/src/components/live/index.tsx index c93ab578..219c158e 100644 --- a/apps/web/src/components/live/index.tsx +++ b/apps/web/src/components/live/index.tsx @@ -1,15 +1,38 @@ +import { useNavigate, useParams } from '@tanstack/react-router'; +import { useEffect } from 'react'; + import ControlBar from '@/components/live/ControlBar'; import StreamView from '@/components/live/StreamView'; +import { useEndTicle } from '@/hooks/api/live'; +import { useTicle } from '@/hooks/api/ticle'; import useMediasoup from '@/hooks/mediasoup/useMediasoup'; function MediaContainer() { useMediasoup(); + const { ticleId } = useParams({ from: '/_authenticated/live/$ticleId' }); + const navigate = useNavigate({ from: '/live/$ticleId' }); + + const { data: ticleData } = useTicle(ticleId); + const { mutate: endTicleMutate } = useEndTicle(); + + const isOwner = ticleData?.isOwner || false; + const hanleTicleEnd = () => { + endTicleMutate(ticleId); + }; + + useEffect(() => { + if (ticleData?.ticleStatus === 'closed') { + alert('종료된 티클입니다.'); // TODO: toast로 교체 + navigate({ to: '/' }); + } + }, [ticleData?.ticleStatus, navigate]); + return (
- +
); diff --git a/apps/web/src/hooks/api/live.ts b/apps/web/src/hooks/api/live.ts index 8eb36356..fcf7259f 100644 --- a/apps/web/src/hooks/api/live.ts +++ b/apps/web/src/hooks/api/live.ts @@ -7,8 +7,10 @@ export const useEndTicle = () => { return useMutation({ mutationFn: endTicle, - onSuccess: () => { + onSuccess: (_, ticleId) => { queryClient.invalidateQueries({ queryKey: ['appliedTicleList'] }); + queryClient.invalidateQueries({ queryKey: ['ticleList'] }); + queryClient.invalidateQueries({ queryKey: ['ticle', ticleId] }); }, }); }; From d02e5c08da19b66b922f5085cbb9a197a479c053 Mon Sep 17 00:00:00 2001 From: Eunseo Sim <55528304+simeunseo@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:58:19 +0900 Subject: [PATCH 029/167] =?UTF-8?q?design:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20CTA=20=EB=B2=84=ED=8A=BC=20width?= =?UTF-8?q?=20=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/components/dashboard/apply/TicleInfoCard.tsx | 2 +- apps/web/src/components/dashboard/open/TicleInfoCard.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx b/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx index fd1bcb68..242dfedc 100644 --- a/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/apply/TicleInfoCard.tsx @@ -48,7 +48,7 @@ function TicleInfoCard({ {`${dateStr} ${timeRangeStr}`} - diff --git a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx index 3cd8364a..c23bd7c6 100644 --- a/apps/web/src/components/dashboard/open/TicleInfoCard.tsx +++ b/apps/web/src/components/dashboard/open/TicleInfoCard.tsx @@ -68,6 +68,7 @@ function TicleInfoCard({ ticleId, ticleTitle, startTime, endTime, status }: Ticl